int和char在getchar / fgetc和putchar / fputc之间的区别?

我想自己学习C,而且我对getcharputchar感到困惑:

1

 #include <stdio.h> int main(void) { char c; printf("Enter characters : "); while((c = getchar()) != EOF){ putchar(c); } return 0; } 

2

 #include <stdio.h> int main(void) { int c; printf("Enter characters : "); while((c = getchar()) != EOF){ putchar(c); } return 0; } 

C库函数int putchar(int c)将参数char指定的字符(unsigned char)写入stdout。

C库函数int getchar(void) )从stdin获取一个字符(一个无符号字符)。 这相当于以stdin作为参数的getc。

这是否意味着putchar()接受intchar或者其中之一,而getchar()是否应该使用intchar

TL; DR:

  • char c; c = getchar();错误的,破碎和越野车
  • int c; c = getchar();正确的

这也适用于getcfgetc ,如果不是更多的话,因为经常会读到文件的末尾。


总是将getcharfgetcgetc …)(和putchar )的返回值存储到inttypes的variables中。

putchar参数可以是intcharsigned charunsigned char ; 它的types并不重要,所有这些都是一样的,即使可能导致积极的和其他负面的整数通过以上的字符,包括\200 (128)。


必须使用int来存储getcharputchar返回值的原因是,当达到文件结束条件(或发生I / O错误)时,它们都会返回macrosEOF的值是一个负整数常量, (通常是-1 ) 。

对于getchar ,如果返回值不是EOF ,则将读取的unsigned char零扩展为int 。 也就是说,假定8位字符,返回的值可以是0255或macrosEOF的值; 再次假定8位字符,没有办法将这257个不同的值压缩到256中,这样每个字符都可以被唯一标识。


现在,如果将其存储为char ,则效果将取决于字符types是默认签名还是未签名 ! 这从编译器到编译器,架构到架构都不相同。 如果char被签名并假定EOF被定义为-1 ,则input上的EOF和字符'\377'都将等于EOF ; 他们会被签名扩展到(int)-1

另一方面,如果char是无符号的(因为在ARM处理器(包括Raspberry PI系统 )中是默认的),所以没有可以存储在c中的值,其将等于-1 ; 包括EOF ; 而不是在EOF ,您的代码将输出一个\377字符。

这里的危险是,使用带符号的char代码似乎是正常工作,即使它仍然是可怕的破碎 – 合法的input值之一被解释为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; } 

现在我们用signed char运行它:

 % gcc test.c && ./a.out Enter characters : sfdasadfdsaf sfdasadfdsaf ^D % 

似乎是正确的。 但是用unsigned char

 % gcc test.c -funsigned-char && ./a.out Enter characters : Hello world Hello world                            ^C % 

也就是说,我试着按Ctrl-D多次,但是每个EOF都打印了一个instead,而不是打断循环。

现在再次,对于签名的char情况下,它不能区分char 255和EOF在Linux上,打破它的二进制数据等:

 % gcc test.c && echo -e 'Hello world\0377And some more' | ./a.out Enter characters : Hello world % 

只有到“ \0377转义的第一部分被写入stdout。


请注意,字符常量与包含无符号字符值的int之间的比较可能无法按预期工作(例如,ISO 8859-1中的字符常量'ä'意味着有符号值-28 ,所以假设您编写的代码将读取input直到ISO 8859-1代码页中的'ä' ,你会这样做

 int c; while((c = getchar()) != EOF){ if (c == (unsigned char)'ä') { /* ... */ } } 

由于整数提升,所有的char值适合int ,并在函数调用时自动提升,因此你可以给intcharsigned charunsigned char任何一个以putchar作为参数 (不存储它的返回值),它会按预期工作。

以整数传递的实际值可能是正值甚至负值; 例如字符常量\377在8位字符系统上是负的 ,其中char是有符号的; 然而putchar (或者fputc实际上)会将这个值转换为一个无符号的字符。

总是使用int来保存getchar()字符,因为EOF常量是inttypes的。 如果使用char那么与EOF的比较是不正确的。

你可以安全地将char传递给putchar()因为它会被自动提升为int

注意 :在大多数情况下,在技术上使用char会起作用,但是不能有0xFF字符,因为types转换将被解释为EOF 。 为了覆盖所有的情况, 总是使用int 。 正如@Ilja所说 – int需要表示所有256个可能的字符值 EOF ,这是总共257个可能的值,不能以chartypes存储。