我正在尝试自己学习C,我有点困惑getchar和putchar:
#include <stdio.h>
int main(void)
{
char c;
printf("Enter characters : ");
while((c = getchar()) != EOF){
putchar(c);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
#include <stdio.h>
int main(void)
{
int c;
printf("Enter characters : ");
while((c = getchar()) != EOF){
putchar(c);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
C库函数int putchar(int c)将参数char指定的字符(unsigned char)写入stdout.
C库函数int getchar(void)从stdin获取一个字符(一个unsigned char).这相当于以stdin作为参数的getc.
这是否意味着putchar()同时接受int和char或其中一方以及getchar()我们应该使用一个int或char?
Ant*_*ala 44
TL; DR:
char c; c = getchar();是错的,破碎和马车.int c; c = getchar();是正确的.这适用于getc,fgetc甚至更多,因为人们经常会读到文件结尾.
始终将getchar(fgetc,getc...)(和putchar)的返回值最初存储到类型的变量中int.
的参数,以putchar可以是任意的int,char,signed char或unsigned char; 它的类型并不重要,并且所有这些都是相同的,即使一个可能导致正整数和其他负整数被传递给上面的字符并包括\200(128).
为什么你的原因,必须使用int存储返回值两者的getchar和putchar是,当到达档案结尾条件(或发生I/O错误),他们都返回宏的值EOF是负整数常数,(通常-1).
因为getchar,如果返回值不是EOF,则读取unsigned char零扩展为a int.也就是说,假设8位字符,返回的值可以是0...... 255或宏的值EOF; 再次假设8位字符,没有办法将这257个不同的值压缩为256,以便可以唯一地识别它们中的每一个.
现在,如果您将其存储char,则效果将取决于默认情况下字符类型是有符号还是无符号!这从编译器到编译器,架构到架构各不相同.如果char已签名并且假定EOF被定义为-1,那么输入中的两个 EOF和字符'\377'将比较等于EOF; 他们会被签名延伸到(int)-1.
另一方面,如果char是无符号的(因为它默认存在于ARM处理器上,包括Raspberry PI系统 ;对于AIX来说似乎也是如此),没有可以存储的值可以c比较等于-1; 包括EOF; 而不是打破EOF,你的代码将输出一个\377字符.
这里的危险是,使用signed chars代码似乎正常工作,即使它仍然可怕地被破坏 - 其中一个合法的输入值被解释为EOF.此外,C89,C99,C11并未规定值EOF; 它只说EOF是一个负整数常数; 因此,而不是-1它可以说-224在特定的实现上,这会导致空间表现得像EOF.
gcc有一个开关-funsigned-char,可用于char在默认签名的平台上进行无符号操作:
% cat test.c
#include <stdio.h>
int main(void)
{
char c;
printf("Enter characters : ");
while((c= getchar()) != EOF){
putchar(c);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我们使用signed运行它char:
% gcc test.c && ./a.out
Enter characters : sfdasadfdsaf
sfdasadfdsaf
^D
%
Run Code Online (Sandbox Code Playgroud)
似乎工作正常.但是没有签名char:
% gcc test.c -funsigned-char && ./a.out
Enter characters : Hello world
Hello world
???????????????????????????^C
%
Run Code Online (Sandbox Code Playgroud)
也就是说,我试图按下Ctrl-D那里很多次,但是?每个都打印出来EOF而不是打破循环.
现在,再次,对于已签名的char案例,它无法区分char255和EOFLinux,打破二进制数据等等:
% gcc test.c && echo -e 'Hello world\0377And some more' | ./a.out
Enter characters : Hello world
%
Run Code Online (Sandbox Code Playgroud)
只有\0377逃脱的第一部分被写入stdout.
请注意,字符常量和int包含无符号字符值的比较可能无法按预期工作(例如'ä',ISO 8859-1中的字符常量将表示有符号值-28.因此,假设您编写的代码将'ä'在ISO 8859-1中读取输入代码页,你做的
int c;
while((c = getchar()) != EOF){
if (c == (unsigned char)'ä') {
/* ... */
}
}
Run Code Online (Sandbox Code Playgroud)
由于整型提升,所有的char价值观匹配到int,并且在函数调用自动提升,从而你可以给任何的int,char,signed char或unsigned char以putchar作为参数(不保存其返回值),并预期它会工作.
在整数中传递的实际值可能是正数甚至是负数; 例如,在签名的8位字符系统中,字符常量\377将为负char ; 但是putchar(或fputc实际上)会将值转换为unsigned char.C11 7.21.7.3p2:
2 fputc函数将由
c(转换为unsigned char)指定的字符写入stream [...]指向的输出流
(强调我的)
即fputc保证将转换给定的,c如同(unsigned char)c