使用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 char
和int
具有相同数量的唯一值时。 这种情况发生在罕见的平台上,包括char
, signed char
, unsigned char
, short
, unsigned short
, int
, unsigned
所有这些都使用相同的位宽和范围宽度。
请注意, 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
在成功读取时返回“转换为int
的unsigned 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
函数将该字符作为转换为int
的unsigned 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位字节)