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

我们经常使用这样的fgetc

 int c; while ((c = fgetc(file)) != EOF) { // do stuff } 

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

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

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

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

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

 int c; for (;;) { c = fgetc(file); if (feof(file)) break; // do stuff } 

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

是的, c = fgetc(file); if (feof(file)) 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 } 

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

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

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


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

  • §5.2.4.2.1整数类型的大小

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

    […]

    • int类型对象的最小值

      INT_MIN -32767

    • int类型对象的最大值

      INT_MAX +32767

  • §7.19.1 – 简介

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

  • §7.19.7.1fgetsfunction

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

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

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

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

注意:在最常见的情况下,chux的答案是正确的。 我将这个答案留下来,因为我相信评论中的答案和讨论对于理解chux的方法是必要的(罕见)情况是有价值的。

EOF保证具有负值(C99 7.19.1),如您所述,fgetc在转换为int之前将其输入读取为unsigned char。 所以那些自己保证不能从文件中读取EOF。

至于你的具体问题:

  • fgetc无法读取等于EOF的合法数据。 在文件中,没有签名或未签名的东西; 它只是位序列。 它是C,它以不同的方式解释1000 1111,具体取决于它是被视为有符号还是无符号。 fgetc需要将其视为无符号,因此无法返回负数(EOF除外)。

    附录:它无法读取unsigned char部分的EOF,但是当它将unsigned char转换为int时,如果int不能表示unsigned char的所有值,则行为是实现定义的(6.3。 1.3)。

  • fgetc是托管实现的标准所必需的,但允许独立实现省略大多数标准库函数(有些显然是必需的,但我找不到列表。)

  • EOF不需要很长时间,因为fgetc需要能够返回它并且fgetc返回一个int。

  • 就改变数据而言,它不能完全改变 ,但由于fgetc被指定为从文件中读取“字符”而不是字符,因此即使系统也可能一次读取8位否则将CHAR_BIT定义为16(如果sizeof(int)== 1,则它是最小值,因为5.2.4.2需要INT_MIN <= -32767和INT_MAX> = 32767)。 在这种情况下,输入字符将转换为一个总是有高位0的无符号字符。然后它可以转换为int而不会丢失精度。 (实际上,这不会出现,因为机器一般不具有16位字节)