[英]Why argument type of `putchar()`, `fputc()` and `putc()` is not `char`?
[英]Difference between int and char in getchar/fgetc and putchar/fputc?
我正在尝试自己学习 C,但我对getchar
和putchar
有点困惑:
#include <stdio.h>
int main(void)
{
char c;
printf("Enter characters : ");
while((c = getchar()) != EOF){
putchar(c);
}
return 0;
}
#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 指定的字符(无符号字符)写入 stdout。
C 库函数int getchar(void)
) 从 stdin 获取一个字符(无符号字符)。 这相当于以 stdin 作为参数的 getc。
这是否意味着putchar()
接受int
和char
或它们中的任何一个,对于getchar()
我们应该使用int
还是char
?
特尔;博士:
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
零扩展到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
; 您的代码将输出一个\\377
字符,而不是在EOF
上爆发。
这里的危险在于,使用 signed char
,代码似乎可以正常工作,即使它仍然严重损坏 - 合法输入值之一被解释为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
都打印了一个
而不是打破循环。
现在,再次,对于有符号char
情况,它无法区分 Linux 上的char
255 和EOF
,将其分解为二进制数据等:
% gcc test.c && echo -e 'Hello world\0377And some more' | ./a.out
Enter characters : Hello world
%
只有\\0377
转义之前的第一部分被写入标准输出。
请注意,字符常量和包含无符号字符值的int
之间的比较可能无法按预期工作(例如,ISO 8859-1 中的字符常量'ä'
表示有符号值-28
。因此假设您编写的代码可以读取输入直到 ISO 8859-1 代码页中的'ä'
,你才会这样做
int c;
while ((c = getchar()) != EOF){
if (c == (unsigned char)'ä') {
/* ... */
}
}
由于整数提升,所有char
值都适合int
,并在函数调用时自动提升,因此您可以将int
、 char
、 signed char
或unsigned char
作为参数提供给putchar
(不存储其返回值),它会按预期工作。
整数中传递的实际值可能是正数,甚至可能是负数; 例如,字符常量\\377
在char
有符号的 8 位字符系统上将是负数; 但是putchar
(或实际上是fputc
)会将值转换为无符号字符。 C11 7.21.7.3p2 :
2 fputc 函数将
c
指定的字符(转换为无符号字符)写入流 [...]
(强调我的)
即fputc
将保证将给定的c
转换为(unsigned char)c
始终使用int
从getchar()
保存字符,因为EOF
常量是int
类型。 如果您使用char
则与EOF
的比较不正确。
您可以安全地将char
传递给putchar()
因为它会自动提升为int
。
注意:从技术上讲,在大多数情况下使用char
会起作用,但是您不能有 0xFF 字符,因为它们会由于类型转换而被解释为EOF
。 要涵盖所有情况,请始终使用int
。 正如@Ilja 所说——需要int
来表示所有 256 个可能的字符值和EOF
,总共有 257 个可能的值,不能以char
类型存储。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.