使用fgetc时,是否可以将EOF与正常的字节值混淆?

ana*_*lyg 4 c binaryfiles language-lawyer fgetc

我们经常使用fgetc这样的:

int c;
while ((c = fgetc(file)) != EOF)
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,如果文件中的某个字节的值为EOF,则此代码存在错误 - 它会提前中断循环并且无法处理整个文件.这种情况可能吗?

据我所知,fgetc内部将从文件读取的字节转换为unsigned char然后int返回,并返回它.如果范围int大于的范围,这将起作用unsigned char.

如果不是(可能那么sizeof(int)=1)会发生什么?

  • 有时会fgetc读取一个等于EOF文件的合法数据吗?
  • 它会改变从文件中读取的数据以避免单个值EOF吗?
  • fgetc是一个未实现的功能?
  • EOF是另一种类型long吗?

我可以通过额外的检查使我的代码变得简单:

int c;
for (;;)
{
    c = fgetc(file);
    if (feof(file))
        break;
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

如果我想要最大的便携性,这是必要的吗?

nne*_*neo 5

C规范说,int必须至少能够保持-32767到32767之间的值.任何较小的平台int都是非标准的.

C规范还说这EOF是一个负int常数,并且在成功读取时fgetc返回" unsigned char转换为int".由于unsigned char不能具有负值,因此EOF可以将值与从流中读取的任何内容区分开来.*

*请参见下文,了解未能解决的漏洞案例.


相关标准文本(来自C99):

  • §5.2.4.2.1整数类型的大小<limits.h>:

    []实现定义的值的大小(绝对值)应等于或大于所示的值,并带有相同的符号.

    [...]

    • 类型对象的最小值 int

      INT_MIN -32767

    • 类型对象的最大值 int

      INT_MAX +32767

  • §7.19.1 <stdio.h>- 简介

    EOF...扩展为一个整数常量表达式,带有类型int和负值,由几个函数返回以指示文件结束,即不再有来自流的输入

  • §7.19.7.1 fgets函数

    如果stream未设置指向的输入流的文件结束指示符并且存在下一个字符,则该fgetc函数将该字符作为unsigned char转换为a获得int并前进该流的相关文件位置指示符(如果已定义)

如果为UCHAR_MAXINT_MAX,则没有问题:所有unsigned char值都将转换为非负整数,因此它们将与EOF不同.

现在,这里一个有趣的漏洞:如果系统有 UCHAR_MAX> INT_MAX,那么法律允许系统将值转换为大于INT_MAX负整数的值(根据§6.3.1.3,将值转换为无法签名的类型的结果表示该值是实现定义的),使得从流中读取的字符可以转换为EOF.

CHAR_BIT > 8确实存在的系统(例如TI C4x DSP,显然使用32位字节),虽然我不确定它们是否在EOF和流功能方面有所破坏.

  • 啊,但是`CHAR_BIT`可以大于8,而对于`CHAR_BIT> = 16`,允许`sizeof(int)`为1. (4认同)

chu*_*ica 5

是的,c = fgetc(file); if (feof(file))确实可以实现最大的便携性。它适用于一般情况,也适用于unsigned charint具有相同数量的唯一值时。这发生在与罕见的平台charsigned charunsigned charshortunsigned shortintunsigned全部采用相同的位宽度和范围宽度。

注意feof(file))是不够的。代码还应检查ferror(file).

int c;
for (;;)
{
    c = fgetc(file);
    if (c == EOF) {
      if (feof(file)) break;
      if (ferror(file)) break;
    }
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管这对 * 绝大多数 * 系统来说有点过分,但基于其他答案评论中的讨论,我现在确信这是唯一 100% 可移植的方法,以及实际 * 托管的想法* sizeof(int) == 1 的实现现在将永远困扰着我的噩梦。 (3认同)