我的观点是,如果stdio
,{C}实现不能满足某些fputc
函数的规范(特别是fgetc
/ sizeof(int)==1
),因为int
需要能够保留unsigned char
或EOF
( - 1)的任何可能值。这种推理是否正确?
(显然sizeof(int)
如果CHAR_BIT
为8则不能为1,因为int
需要的最小范围,所以我们隐含地只讨论CHAR_BIT>=16
的实现,例如DSP,其中典型的实现是独立实现而不是托管实现,因此不需要提供stdio
。)
修改:在阅读了答案和一些链接参考后,对托管实施可能对sizeof(int)==1
有效的方式提出了一些想法:
首先,一些引用:
7.19.7.1(2-3):
如果没有设置stream指向的输入流的结束指示符,并且a 如果存在下一个字符,则fgetc函数将该字符作为无符号获取 char转换为int并前进相关的文件位置指示器 流(如果已定义)。
如果设置了流的结束指示符,或者流处于文件结尾,则设置流的结束指示符并且fgetc函数返回EOF。否则, fgetc函数返回stream指向的输入流中的下一个字符。 如果发生读取错误,则设置流的错误指示符和fgetc函数 返回EOF。
7.19.8.1(2):
fread函数读入ptr指向的数组,最多读取nmemb元素 其大小由大小指定,来自流指向的流。对于每一个 对象,对fgetc函数进行大小调用,并按顺序存储结果 读取,在一个无符号字符数组中正好覆盖对象。文件位置 流的指示符(如果已定义)按成功读取的字符数提前。
思想:
回读unsigned char
范围之外的int
值可能只是在实现中具有 undefined 实现定义的行为。这尤其令人不安,因为它意味着使用fwrite
和fread
来存储二进制结构(当它导致不可移植的文件时,应该是可以在任何单个实现上移植的操作)似乎工作,但默默地失败。 基本上总是导致未定义的行为。我接受一个实现可能没有可用的文件系统,但是很难接受一个实现可能有一个文件系统,一旦你尝试使用它就会自动调用鼻子恶魔,并且无法确定它是不可用的。 / strike>现在我意识到行为是实现定义的而不是未定义的,它并不是那么令人不安,我认为这可能是一个有效的(尽管是不合需要的)实现。
实现sizeof(int)==1
可以简单地将文件系统定义为空且只读。然后,应用程序无法读取自己编写的任何数据,只能从stdin
上的输入设备读取,这可以实现为只提供适合char
的正int
值}。
编辑(再次):从C99原理,7.4:
EOF传统上是-1,但可以是任何负整数,因此可以与任何有效的字符代码区分开来。
这似乎表明sizeof(int)
可能不是1,或者至少是委员会的意图。
答案 0 :(得分:24)
即使fgetc
,实现也可能满足fputc
和sizeof(int) == 1
的接口要求。
fgetc
的界面表示它将读取的字符作为unsigned char
转换为int
。没有任何地方说这个值不能是EOF
,即使期望显然有效读数“通常”返回正值。当然,fgetc
在读取失败或流结束时返回EOF
,但在这些情况下,还会设置文件的错误指示符或文件结束指示符(分别)。
同样,只要它恰好与EOF
转换为fputc
的值一致,就无法将unsigned char
传递给int
。 }。
显然程序员必须在这样的平台上非常小心。这可能不是完整的副本:
void Copy(FILE *out, FILE *in)
{
int c;
while((c = fgetc(in)) != EOF)
fputc(c, out);
}
相反,你必须做类似的事情(未经测试!):
void Copy(FILE *out, FILE *in)
{
int c;
while((c = fgetc(in)) != EOF || (!feof(in) && !ferror(in)))
fputc(c, out);
}
当然,您遇到实际问题的平台是那些sizeof(int) == 1
并且从unsigned char
到int
的转换不是注入的平台。我相信在使用符号和幅度的平台上或者对于有符号整数的表示补码时,情况必然如此。
答案 1 :(得分:10)
我在10年或15年前的comp.lang.c上记得这个完全相同的问题。搜索它,我在这里找到了一个更新的讨论:
http://groups.google.de/group/comp.lang.c/browse_thread/thread/9047fe9cc86e1c6a/cb362cbc90e017ac
我认为有两个结果:
(a)可以实现严格一致性的实施。例如。 sizeof(int)== 1,带有一个补码或符号大小的负值或int类型中的填充位,即并非所有无符号字符值都可以转换为有效的int值。
(b)典型的习语((c=fgetc(in))!=EOF)
不可移植(CHAR_BIT == 8除外),因为EOF不需要是单独的值。
答案 2 :(得分:5)
我不相信C标准直接要求EOF与可以从流中读取的任何值不同。与此同时,它似乎也是理所当然的。标准的某些部分有相互冲突的要求,如果EOF是可以从流中读取的值,我怀疑是否可以满足。
例如,考虑ungetc
。一方面,规范说(§7.19.7.11):
ungetc函数推送c指定的字符(转换为unsigned char)返回stream指向的输入流。推回字符将是 后续读取该流的返回按其推送的相反顺序返回。 [...] 保证了一个回击的特征。
另一方面,它也说:
如果c的值等于宏EOF的值,则操作失败,输入流不变。
因此,如果EOF是一个可以从流中读取的值,并且(例如)我们从流中读取,并立即使用ungetc
将EOF放回到流中,我们就会遇到一个难题:呼叫“保证”成功,但也明确要求失败。
除非有人能够找到一种方法来协调这些要求,否则我对这样的实施是否符合这一点仍然存在相当大的疑问。
如果有人关心,N1548(新C标准的当前草案)保留了相同的要求。
答案 3 :(得分:3)
如果与 char
共享位模式的名义EOF
被定义为非敏感,那还不够吗?例如,如果CHAR_BIT为16,但所有允许值仅占用15个最低有效位(假设符号 - 幅度int表示的2s补码)。或者char
中的所有内容都必须具有这样的含义?我承认我不知道。
当然,那将是一个奇怪的野兽,但我们让我们的想象力在这里,对吧?
R ..让我确信这不会在一起。因为托管实现必须实现stdio.h
并且如果fwrite
能够在磁盘上粘贴整数,那么fgetc
可以返回适合char
的任何位模式,这不得干扰返回EOF。 QED。
答案 4 :(得分:2)
我对C99不太熟悉,但我没有看到任何说明fgetc
必须生成char
的全部值的内容。在这样的系统上实现stdio的显而易见的方法是在每个char
中放置8位,而不管其容量如何。 EOF
的要求是
EOF
扩展为整数 常量表达式,类型为int和 一个负值,由...返回 几个功能来表明 文件结束,即不再输入 来自流
情况类似于wchar_t
和wint_t
。在定义wint_t
和WEOF
的7.24.1 / 2-3中,脚注278说
wchar_t
和wint_t
可以是相同的整数类型。
似乎可以保证“软”范围检查足以保证*EOF
不在字符集中。
这不允许使用二进制流,因为在这种情况下,fputc
和fgetc
不需要执行转换。 (7.19.2 / 3)二进制流不是可选的;只有它们与文本流的区别才是可选的。所以看起来这会使这种实现不合规。但是,只要你不尝试在8位范围之外写二进制数据,它仍然是完全可用的。
答案 5 :(得分:2)
我认为你是对的。在二进制流上使用fgetc
/ fputc
时,此类实现无法区分合法的unsigned char值和EOF。
如果有这样的实现(this thread似乎表明存在),则它们严格符合。可以使用sizeof (int) == 1
实现独立实现。
独立实现(C99 4)只需要支持这些头文件中指定的标准库中的功能:< float.h>, < iso646.h>,< limits.h>,< stdarg.h>,< stdbool.h>,< stddef.h>,和 < stdint.h取代。 (注意没有< stdio.h>)。对于DSP或其他嵌入式设备而言,独立式可能更有意义。
答案 6 :(得分:1)
您假设EOF不能是字符集中的实际字符。 如果你允许这个,那么sizeof(int)== 1就可以了。
答案 7 :(得分:1)
我使用的TI C55x编译器具有16位字符和16位int, 包含标准库。该库仅假定一个八位字符集,因此当被解释为字符为值为char的字符时> 255未定义;当写入8位流器件时,最高8位被丢弃:例如,当写入UART时,只有低8位被传送到移位寄存器并输出。