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