getchar/fgetc和putchar/fputc中int和char的区别?

Rag*_*san 25 c

我正在尝试自己学习C,我有点困惑getcharputchar:

1

#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)

2

#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()同时接受intchar或其中一方以及getchar()我们应该使用一个intchar

Ant*_*ala 44

TL; DR:

  • char c; c = getchar();错的,破碎和马车.
  • int c; c = getchar();正确的.

这适用于getc,fgetc甚至更多,因为人们经常会读到文件结尾.


始终将getchar(fgetc,getc...)(和putchar)的返回值最初存储到类型的变量中int.

参数,以putchar可以是任意的int,char,signed charunsigned char; 它的类型并不重要,并且所有这些都是相同的,即使一个可能导致正整数和其他负整数被传递给上面的字符并包括\200(128).


为什么你的原因,必须使用int存储返回值两者的getcharputchar是,当到达档案结尾条件(或发生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 charunsigned charputchar作为参数(不保存其返回值),并预期它会工作.

在整数中传递的实际值可能是正数甚至是负数; 例如,在签名的8位字符系统中,字符常量\377将为char ; 但是putchar(或fputc实际上)会将值转换为unsigned char.C11 7.21.7.3p2:

2 fputc函数将由c (转换为unsigned char)指定的字符写入stream [...]指向的输出流

(强调我的)

fputc保证将转换给定的,c如同(unsigned char)c

  • 特别是如果你住在土耳其,那里使用字母ÿ(y-umlaut,U + 00FF,LATIN SMALL LETTER Y WITH DIAERESIS),然后在代码中键入该字母,将`getchar()`的结果保存为签名的` char`类型将被检测为EOF,就像您键入Control-D(Unix)或Control-Z(Windows) - 那些表示'没有更多数据'或EOF.因此,问题是合法的字符(ÿ)在不应该被视为EOF时被视为.它几乎和从未将任何东西视为EOF一样糟糕. (3认同)