7

编译以下代码时,它会进入无限循环:

int main()
{
    unsigned char  ch;
    FILE *fp;
    fp = fopen("abc","r");
    if(fp==NULL)
    {
        printf("Unable to Open");
        exit(1);
    }
    while((ch = fgetc(fp))!=EOF)
    printf("%c",ch);
    fclose(fp);
    printf("\n",ch);
    return 0;
}

gcc 编译器还会在编译时发出警告

abc.c:13:warning: comparison is always true due to limited range of data type

unsigned char被替换charint按预期(即它终止)时,代码运行良好。
但代码也运行unsigned int良好。正如我所读到的EOF那样-1stdio.h为什么该代码对于 unsigned char 失败但对于 unsigned int 运行良好。

4

6 回答 6

10

写这行的黄金法则是

   while ((ch = fgetc(stdin)) != EOF)

ch应该是int。你制作 unsigned 的可爱技巧ch失败了,因为EOF它是一个有符号的 int 数量。

好的,现在让我们进入深度......

步骤1:

ch=fgetc(fp)

fgetc()返回-1(签名的int)。根据 C 的黄金法则,ch得到最后一个八位位组,即 all 1's。因此价值255ch执行后的字节模式

ch = fgetc(fp); 

因此将是

11111111

第2步:

ch != EOF

NowEOF是一个有 符号整数并且ch是一个unsigned char...

我再次提到 C 的黄金法则......在比较之前,较小的家伙ch被转换为大尺寸int,所以它的字节模式现在是

00000000000000000000000011111111 = (255)10

虽然EOF

11111111111111111111111111111111 = (-1)10

它们不可能相等.......因此,引导以下while循环的语句

while ((ch = fgetc(stdin)) != EOF)

永远不会评估为假...

因此无限循环。

于 2011-12-21T08:26:01.840 回答
7

有几个隐式转换正在进行。它们与特定警告并不真正相关,但我将它们包含在此答案中以显示编译器对该表达式的真正作用。

  • 您的示例中的 ch 是 unsigned char 类型。
  • EOF 保证为 int 类型(C99 7.19.1)。

所以表达式等价于

(unsigned char)ch != (int)EOF

C 中的整数提升规则会将 unsigned char 隐式转换为 unsigned int:

(unsigned int)ch != (int)EOF

然后,C 中的平衡规则(也称为通常的算术转换)会将 int 隐式转换为 unsigned int,因为每个操作数必须具有相同的类型:

(unsigned int)ch != (unsigned int)EOF

在您的编译器上,EOF 可能是-1:

(unsigned int)ch != (unsigned int)-1

其中,假设 32 位 CPU,与

(unsigned int)ch != 0xFFFFFFFFu

一个角色永远不可能有这么高的价值,因此是警告。

于 2011-12-21T08:41:32.357 回答
2

I have encountered this problem too. My solution is to use feof().

unsigned int xxFunc(){
  FILE *fin;
  unsigned char c;
  fin = fopen("...", "rb");
  if(feof(fin) != 0) return EOF;
  c = fgetc(fin);
  fclose(fin);
...
}

And you can define an int variable to compare with EOF. For example:

int flag = xxFunc();
while(flag != EOF) {...}

This works for me.

**IMPORTANT UPDATE***

After using the method I mentioned before, I found a serious problem. feof() is not a good way to break the while loop. Here is the reason for it. http://www.gidnetwork.com/b-58.html

So I find a better way to do this. I use an int variable to do it. here:

int flag;
unsigned char c;
while((flag = fgetc(fin)) != EOF) 
{ 
  //so, you are using flag to receive, but transfer the value to c later.
  c = flag;
  ... 
}

After my test, this works.

于 2013-03-14T23:28:21.743 回答
2

你需要使用一个 int

fgetc() 专门返回一个 int 以便它可以指示文件的结尾

它与有符号字符一起运行良好,因为 EOF (-1) 在范围内,但如果你读入一个值大于 127 的字符,它会中断。

使用 int,在检查 EOF 后将其转换为 char

于 2011-12-21T08:16:16.717 回答
1

当您将无符号整数与有符号整数进行比较时,它会将有符号整数转换为无符号整数并进行比较。因此,当您使用 unsigned int 'ch' 读取文件时,读取 EOF 会为您提供 2^32+1(在 4 字节 int 机器上),并且在将其与 EOF 进行比较时,它将 EOF 转换为 unsigned,这也是 2^ 32+1,因此程序停止!

如果您使用 unsigned char ch,当您读取文件时,读取 EOF 返回 2^32+1,这将被转换为 unsigned char,它将值截断为前 8 位(在 1 字节字符机器上)并为您提供输出为 255。因此,您正在比较 255 和 2^32+1,从而导致无限循环。

这里的问题是在比较之前截断。

如果你使用

while((ch = fgetc(fp))!=(unsigned char)EOF)
    printf("%c",ch);

您的程序将运行良好!

于 2011-12-21T08:40:32.107 回答
0

这种实现会产生一个 lint 警告

将类型“char”与 EOF 进行比较

 // read the data in a buffer
611     ch = getc(csv_file);
612     while (ch != EOF)

使固定:

// read the data in a buffer
    while ((ch = getc(csv_file)) != EOF)
于 2017-05-26T11:48:12.540 回答