免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2563 | 回复: 4
打印 上一主题 下一主题

[C] 关于EOF [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-08 14:11 |只看该作者 |倒序浏览
1:
int
main(void)
{
   printf("%cxxxx\n", 255);
}
./a.out > bx


#include <stdio.h>

2:
int
main(void)
{
  char c = getchar();//int c = getchar()
  if (c == EOF) {
    printf("reach EOF\n");
    if (!feof(stdin))
      printf("not reach EOF\n");
  }
}

./a.out <bx

当2中定义char c = getchar()时,程序执行结果是:
reach EOF
not reach EOF
当定义int c = getchar()时,程序什么也不输出.

哪位给解释一下为什么会出现这样的执行结果?
谢谢!

论坛徽章:
0
2 [报告]
发表于 2008-06-08 15:51 |只看该作者
EOF is returned by an I/O routine when the end-of-file (or in some cases, an error) is encountered.
看到一篇这个问题的讨论: 许多初学者都习惯用 char 型变量接收 getchar、getc,fgetc 等函数的返回值,其实这么做是不对的,并且隐含着足以致命的错误。getchar 等函数的返回值类型都是 int 型,当这些函数读取出错或者读完文件后,会返回 EOF。EOF 是一个宏,标准规定它的值必须是一个 int 型的负数常量。通常编译器都会把 EOF 定义为 -1。问题就出在这里,使用 char 型变量接收 getchar 等函数的返回值会导致对 EOF 的辨认出错,或者错把好的数据误认为是 EOF,或者把 EOF 误认为是好的数据。例如:

        int c;  /* 正确。应该使用 int 型变量接收 fgetc 的返回值 */
        while ( (c = fgetc(fp)) != EOF )
        {
            putchar(c);
        }

如上例所示,我们很多时候都需要先用一个变量接收 fgetc 等函数的返回值,然后再用这个变量和 EOF 比较,判断是否已经读完文件。上面这个例子是正确的,把 c 定义为 int 型保证了它能正确接收 fgetc 返回的 EOF,从而保证了这个比较的正确性。但是,如果把 c 定义为 char 型,则会导致意想不到的后果。

    首先,因为 fgetc 等函数的返回值是 int 型的,当赋值给 char 型变量时,会发生降级,从而导致数据截断。例如:

                  ---------------------------------
                  | 十进制 |      int     |  char |
                  |--------|--------------|-------|
                  |   10   | 00 00 00 0A  |   0A  |
                  |   -1   | FF FF FF FF  |   FF  |
                  |   -2   | FF FF FF FE  |   FE  |
                  ---------------------------------

在此,我们假设 int 和 char 分别是 32 位和 8 位的。由上表可得,从 int 型到 char 型,损失了 3 个字节的数据。而当我们要拿 char 型和 int 型比较的时候,char 型会自动升级为 int 型。char 型升级为 int 型后的值会因为它到底是 signed char 还是 unsigned char 而有所不同。不幸的是,如果我们没有使用 signed 或者 unsigned 来修饰 char,那么我们无从知晓 char 到底是指 unsigned char 还是指 signed char,因为这是由编译器决定的。不过,无论 char 是 signed 的也好,unsigned 的也罢,都不能改变使用 char 型变量接收 fgetc 等函数的返回值是错误的这个事实。唯一能改变的是该错误导致的后果。前面我们说了,char 型和 int 型比较时,char 会自动升级为 int,下面我们来看看 signed char 和 unsigned char 在转换成 int 后,它们的值有什么不同:

                  ---------------------------------------
                  |  char |   unsigned    |   signed    |
                  |-------|---------------|-------------|
                  |  10   |  00 00 00 0A  | 00 00 00 0A |
                  |  FF   |  00 00 00 FF  | FF FF FF FF |
                  |  FE   |  00 00 00 FE  | FF FF FF FE |
                  ---------------------------------------

由上表可知,当 char 是 unsigned 的时候,其转换为 int 后的值是正数。也就是说,假如我们把 c 定义为 char 型变量,而编译器默认 char 为 unsigned char,那么以下表达式将永远成立。

        (c = fgetc(fp)) != EOF  /* c 的值永远为正数,而标准规定 EOF 为负数 */

也就是说以下循环是一个死循环。

        while ( (c = fgetc(fp)) != EOF )
        {
            putchar(c);
        }

    读到这里,可能有些读者朋友会说:“那么我明确把 c 定义为 signed char 型的就没问题了吧!”很遗憾,就算把 c 定义为 signed char,仍然是错误的。假设 fgetc 等函数读到一个字节的值为 FF,那么返回值就是 00 00 00 FF。把这个值赋值给 c 后, c 的值变成 FF。然后 c 的值为了和 EOF 比较,会自动升级为 int 型的值,也就是 FF FF FF FF。从而导致以下表达式不成立。

        (c = fgetc(fp)) != EOF  /* 读到值为 FF 的字符,误认为 EOF */

也就是说以下循环在没有读完文件的情况下提前退出。

        while ( (c = fgetc(fp)) != EOF )
        {
            putchar(c);
        }

    综上所述,使用 char 型变量接收 fgetc 等函数的返回值是错误的,我们必须使用 int 型变量接收这些函数的返回值,然后判断接收到的值是否 EOF。只有判断发现该返回值并非 EOF,我们才可以把该值赋值给 char 型变量。

论坛徽章:
0
3 [报告]
发表于 2008-06-09 19:11 |只看该作者

回复 #3 xiaozhu2007 的帖子

呵呵,其实有问题先google是一种捷径

论坛徽章:
0
4 [报告]
发表于 2008-06-09 23:36 |只看该作者

回复 #4 ruoyisiyu 的帖子

学习了,其实这个问题是我google的一篇文章中的 一个例子,当时没想明白,比较笨也比较懒呵呵。

论坛徽章:
0
5 [报告]
发表于 2008-06-09 23:56 |只看该作者
老实说,这个在精华里有。而且有一篇说getchar()函数的。很是经典。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP