【问题标题】:Comparing unsigned char and EOF比较 unsigned char 和 EOF
【发布时间】:2012-01-25 01:58:10
【问题描述】:

以下代码编译后进入无限循环:

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 中所读到的那样,在stdio.h 中定义为-1 那么为什么此代码对于unsigned char 失败但对于unsigned int 运行良好。

【问题讨论】:

标签: c comparison eof unsigned-char fgetc


【解决方案1】:

你需要使用一个int

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

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

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

【讨论】:

  • 我知道 int 应该在正确的代码中使用,但我想知道为什么 unsigned char 不起作用但 unsigned int 起作用......
  • -1 如何在无符号整数范围内
  • 阅读整数提升规则。
【解决方案2】:

写这行的黄金法则是

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

ch 应该是 int 。使 ch 无符号的可爱技巧失败了,因为 EOF 是有符号整数。

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

第 1 步:

ch=fgetc(fp)

fgetc() 返回-1(签名的int)。根据 C 的黄金法则,ch 得到最后一个八位字节,这都是1 的。因此值255

执行后ch 的字节模式
ch = fgetc(fp); 

应该是

11111111

第 2 步:

ch != EOF

现在EOF 是一个有符号整数ch 是一个unsigned char ...

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

00000000000000000000000011111111 = (255)10

EOF

11111111111111111111111111111111 = (-1)10

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

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

永远不会评估为假...

因此是无限循环。

【讨论】:

  • 黄金法则是始终匹配括号。您在两个示例中都缺少),它必须是while((ch=fgetc(stdin))!=EOF)
  • 我只是让你的明确答案看起来更好一些。不过,您能否详细说明一下您想用这种模式表达什么:... = (255)10... = (-1)10
  • @alk 这个答案是由“学生我”写的,因此符号不一致。 (255)10 表示以 10 为底的 255
【解决方案3】:

当您将无符号整数与有符号整数进行比较时,它会将有符号整数转换为无符号整数并进行比较。因此,当您使用 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);

你的程序会运行良好!

【讨论】:

    【解决方案4】:

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

    • 在您的示例中,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
    

    一个字符永远不可能有这么高的值,因此警告。

    【讨论】:

      【解决方案5】:

      我也遇到过这个问题。我的解决方案是使用 feof()。

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

      您可以定义一个 int 变量来与 EOF 进行比较。例如:

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

      这对我有用。

      **重要更新***

      使用我之前提到的方法后,我发现了一个严重的问题。 feof() 不是打破 while 循环的好方法。 这是它的原因。 http://www.gidnetwork.com/b-58.html

      所以我找到了一个更好的方法来做到这一点。我使用一个 int 变量来做到这一点。这里:

      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;
        ... 
      }
      

      经过我的测试,这是可行的。

      【讨论】:

        【解决方案6】:

        这种实现会产生 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)
        

        【讨论】:

          猜你喜欢
          • 2011-07-02
          • 2013-06-15
          • 2017-03-13
          • 1970-01-01
          • 2015-07-10
          • 1970-01-01
          • 1970-01-01
          • 2020-07-06
          • 2013-06-24
          相关资源
          最近更新 更多