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

时间:2016-02-12 06:39:32

标签: c

我正在尝试自己学习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获取一个字符(unsigned char)。这相当于以stdin作为参数的getc。

这是否意味着putchar()接受intchar或其中任何一个,getchar()我们应该使用intchar

2 个答案:

答案 0 :(得分:43)

TL; DR:

  • char c; c = getchar(); 错误,破坏和错误
  • int c; c = getchar(); 正确

这同样适用于getcfgetc,如果不是这样的话,因为人们经常会读到文件的结尾。

始终将getcharfgetcgetc ...)(和putchar)的返回值最初存储到int类型的变量中。

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

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

对于getchar,如果返回值不是EOF,则读取 unsigned char 零扩展为int。也就是说,假设8位字符,返回的值可以是0 ... 255或宏EOF的值;再次假设8位字符,没有办法将这257个不同的值压缩为256,这样就可以唯一地识别每个值。

现在,如果您将其存储到char中,效果将取决于the character type is signed or unsigned by default!这从编译器到编译器,架构到架构各不相同。如果char已签名且假定EOF定义为-1,那么输入中的 EOF和字符'\377'将相等到EOF;他们会被签名延伸到(int)-1

另一方面,如果char是无符号的(因为它默认存在于ARM处理器上,包括Raspberry PI systems;并且AIX too似乎是真的),那么值可以存储在c中,可以比较等于-1;包括EOF;而不是在EOF上突破,您的代码将输出一个\377字符。

这里的危险是,使用签名的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;
}

现在我们使用签名char

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

似乎工作正常。但是使用未签名的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转义的第一部分被写入stdout。

请注意,字符常量与包含无符号字符值的int之间的比较可能无法按预期工作(例如,ISO 8859-1中的字符常量'ä'将表示带符号的值{{1}因此,假设您编写的代码将在ISO 8859-1代码页中读取输入,直到-28,您就可以了

'ä'

由于整数提升,所有int c; while((c = getchar()) != EOF){ if (c == (unsigned char)'ä') { /* ... */ } } 值都适合char,并会在函数调用时自动提升,因此您可以提供intint中的任何一个, charsigned charunsigned char作为参数(不存储其返回值),它会按预期工作。

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

  

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

(强调我的)

即。 c将保证转换给定的fputc,就像c

一样

答案 1 :(得分:3)

始终使用int来保存getchar()中的字符EOF常量为int类型。如果您使用char,则与EOF的比较不正确。

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

注意: 技术上使用char在大多数情况下都可以使用,但是你不能拥有0xFF字符,因为类型转换会将它们解释为EOF。要涵盖所有案例始终,请使用int。正如@Ilja所说 - int需要表示所有256个可能的字符值 EOF,这是总共257个可能的值,不能存储在{ {1}}输入。