8

也许我想多了,因为它似乎应该容易得多。我想取一个 int 类型的值,例如 fgetc() 返回的值,如果它不是文件结尾代码,则将其记录在 char 缓冲区中。例如:

char buf;
int c = fgetc(stdin);

if (c < 0) {
    /* handle end-of-file */
} else {
    buf = (char) c;  /* not quite right */
}

但是,如果平台已签署默认字符,则 fgetc() 返回的值可能超出 char 的范围,在这种情况下,将其强制转换或分配给 (signed) char 会产生实现定义的行为(对吗?)。但是,当然,有大量代码与示例完全相同。这一切都依赖于实现定义的行为和/或假设 7 位数据吗?

在我看来,如果我想确定我的代码行为由 C 定义为我想要的,那么我需要做这样的事情:

buf = (char) ((c > CHAR_MAX) ? (c - (UCHAR_MAX + 1)) : c);

我认为无论默认字符是有符号还是无符号,甚至不管字符的大小如何,都会产生定义的、正确的行为。是对的吗?是否真的需要这样做以确保可移植性?

4

3 回答 3

3

fgetc()返回unsigned char和EOF。EOF 始终 < 0。如果系统charsignedunsigned,则没有区别。

C11dr 7.21.7.1 2

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

我担心的是,这看起来取决于 2 的恭维,并暗示 和 的范围unsigned charchar一样宽。这两个假设在今天几乎总是正确的。

buf = (char) ((c > CHAR_MAX) ? (c - (UCHAR_MAX + 1)) : c);

[根据 OP 评论编辑]
假设在 to 范围内返回的字符fgetc()不比可以填充的字符更多,那么将更便携的替换为. 我们不知道什么时候在范围内。CHAR_MINCHAR_MAX(c - (UCHAR_MAX + 1))(c - CHAR_MAX + CHAR_MIN)(c - (UCHAR_MAX + 1))c is CHAR_MAX + 1

可能存在一个signed char范围为 -127 到 +127 和范围为 0 到 255 的系统unsigned char。(5.2.4.2.1),但是作为fgetc()一个字符,它似乎已经全部unsigned char 全部准备好将自身限制在较小的signed char范围内, 在转换unsigned char为该值并将其返回给用户之前。OTOH,如果fgetc()返回 256 个不同的字符,则无论公式如何,转换为窄范围signed char都是不可移植的。

于 2013-10-08T14:38:01.257 回答
3

实际上,这很简单——显而易见的演员阵容char总是有效的。
但是你问的是便携性......

我看不出真正的便携式解决方案如何工作。
这是因为 的保证范围char是 -127 到 127,也就是只有 255 个不同的值。那么如何在不丢失信息的情况下将fgetc(不包括EOF)的 256 个可能的返回值转换为 a 呢?char

我能想到的最好的方法是使用unsigned char和避免char.

于 2013-10-08T15:17:22.377 回答
2

感谢那些做出回应的人,并且现在已经阅读了 C99 标准的相关部分,我已经同意一个有点令人惊讶的结论,即存储fgetc()as 类型返回的任意非 EOF 值char而不损失保真度并不能保证是可能的. 在很大程度上,这是由于char无法表示与 一样多的不同值的可能性unsigned char

就他们而言,stdio 函数保证如果数据被写入(二进制)流并随后读回,那么读回的数据将与原始数据进行比较。事实证明,它的含义比我最初想象的要窄得多,但这确实意味着fputs()必须为它成功输出的每个不同输出一个不同的值char,并且无论转换fgets()适用于将输入字节存储为类型,都char必须准确地反转转换,如果有的话,由此fputs()将产生输入字节作为其输出。然而,据我所知,fputs()并且fgets()允许在他们不喜欢的任何输入上失败,因此不确定 fputs() 是否将每个可能的char值映射到unsigned char.

此外,虽然fputs()fgets()就像分别执行fputc()fgetc()调用的序列一样操作,但没有指定它们可能char在内存中的unsigned char值和流上的基础值之间执行什么转换。 但是,如果平台fputs()为此目的使用标准整数转换,那么正确的反向转换就是我建议的:

int c = fgetc(stream);
char buf;

if (c >= 0) buf = (char) ((c > CHAR_MAX) ? (c - (UCHAR_MAX + 1)) : c);

这直接来自整数转换规则,该规则指定通过添加或减去 <target type>_MAX + 1 的整数倍数将整数值转换为无符号类型,以将结果带入目标类型的范围,由整数类型表示的约束。它为此目的的正确性不取决于char值的特定表示或是否char被视为有符号或无符号。

但是,如果 char不能表示与 一样多的不同值unsigned char,或者如果存在拒绝输出的charfgets()(例如负值),则可能存在一开始就不可能由转换c产生的值。char没有反向转换参数适用于这些字节,甚至可能没有有意义的char值对应于它们。在任何情况下,给定的转换是否是写入数据的正确反向转换fputs()似乎是实现定义的。是否buf = (char) c会产生相同的效果当然是由实现定义的,尽管它确实在很多系统上都有。

总的来说,我对实现定义了多少 CI/O 行为细节感到震惊。这让我大开眼界。

于 2013-10-09T17:15:19.417 回答