int和char在getchar / fgetc和putchar / fputc之间的区别?
我想自己学习C,而且我对getchar
和putchar
感到困惑:
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()
接受int
和char
或者其中之一,而getchar()
是否应该使用int
或char
?
TL; DR:
-
char c; c = getchar();
是错误的,破碎和越野车 。 -
int c; c = getchar();
是正确的 。
这也适用于getc
和fgetc
,如果不是更多的话,因为经常会读到文件的末尾。
总是将getchar
( fgetc
, getc
…)(和putchar
)的返回值存储到int
types的variables中。
putchar
的参数可以是int
, char
, signed char
或unsigned char
; 它的types并不重要,所有这些都是一样的,即使可能导致积极的和其他负面的整数通过以上的字符,包括\200
(128)。
您必须使用int
来存储getchar
和putchar
的返回值的原因是,当达到文件结束条件(或发生I / O错误)时,它们都会返回macrosEOF
的值是一个负整数常量, (通常是-1
) 。
对于getchar
,如果返回值不是EOF
,则将读取的unsigned char
零扩展为int
。 也就是说,假定8位字符,返回的值可以是0
… 255
或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
,并在函数调用时自动提升,因此你可以给int
, char
, signed char
或unsigned char
任何一个以putchar
作为参数 (不存储它的返回值),它会按预期工作。
以整数传递的实际值可能是正值甚至负值; 例如字符常量\377
在8位字符系统上是负的 ,其中char
是有符号的; 然而putchar
(或者fputc
实际上)会将这个值转换为一个无符号的字符。
总是使用int
来保存getchar()
字符,因为EOF
常量是int
types的。 如果使用char
那么与EOF
的比较是不正确的。
你可以安全地将char
传递给putchar()
因为它会被自动提升为int
。
注意 :在大多数情况下,在技术上使用char
会起作用,但是不能有0xFF字符,因为types转换将被解释为EOF
。 为了覆盖所有的情况, 总是使用int
。 正如@Ilja所说 – int
需要表示所有256个可能的字符值和 EOF
,这是总共257个可能的值,不能以char
types存储。