我们经常使用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
会成为未实现的功能吗?EOF
会是long
的其他类型吗?我可以通过额外的检查使我的代码变得简单:
int c;
for (;;)
{
c = fgetc(file);
if (feof(file))
break;
// do stuff
}
如果我想要最大程度的便携性,这是必要的吗?
答案 0 :(得分: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
-32767int
类型对象的最大值
INT_MAX
+32767
§7.19.1<stdio.h>
- 简介
EOF
...扩展为整数常量表达式,类型为int
,负值,由多个函数返回以指示文件结束,即不再输入来自流
§7.19.7.1fgets
函数
如果
stream
指向的输入流的文件结束指示符未设置且存在下一个字符,则fgetc
函数将该字符作为unsigned char
获取转换为int
并推进流的关联文件位置指示符(如果已定义)
如果UCHAR_MAX
≤INT_MAX
,则没有问题:所有unsigned char
值都将转换为非负整数,因此它们将与EOF区别开来。
现在, 是一个有趣的漏洞:如果系统有UCHAR_MAX
&gt; INT_MAX
,然后法律允许系统将大于INT_MAX
的值转换为负整数(根据§6.3.1.3,将值转换为无法表示该值的有符号类型的结果为实现定义),使从流中读取的字符可以转换为EOF。
确实存在CHAR_BIT > 8
的系统(例如显然使用32位字节的TI C4x DSP),尽管我不确定它们是否在EOF和流功能方面被打破。
答案 1 :(得分:4)
是的,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
}
答案 2 :(得分:0)
注意:在最常见的情况下,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是托管实现的标准所必需的,但是允许独立实现省略大多数标准库函数(有些显然是必需的,但我找不到列表。)
< / LI>EOF不需要很长时间,因为fgetc需要能够返回它并且fgetc返回一个int。
就改变数据而言,它不能完全改变值,但由于fgetc被指定为从文件读取“字符”而不是字符,它可以可能一次读取8位,即使系统另外将CHAR_BIT定义为16(如果sizeof(int)== 1,则它可能具有的最小值,因为INT_MIN&lt; = -32767和INT_MAX&gt; = 32767 5.2.4.2)要求。在这种情况下,输入字符将转换为 unsigned char ,它始终具有高位0.然后它可以转换为int而不会丢失精度。 (实际上,这不会出现,因为机器一般不具有16位字节)