我正在尝试自己学习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 char
s代码似乎正常工作,即使它仍然可怕地被破坏 - 其中一个合法的输入值被解释为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
案例,它无法区分char
255和EOF
Linux,打破二进制数据等等:
% 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