K&R中的类型转换

3

K&R提供了以下关于 getchar() 的示例:

int getchar(void)
{
    char c;

    return (read(0, &c, 1) == 1) ? (unsigned char) c : EOF;
}

在这里将 c 转换为 unsigned char 是为了避免符号扩展问题,但在fputs()示例中...
int fputs(char *s, FILE *iop)
{
    int c;

    while (c = *s++)
        putc(c, iop);
    return ferror(iop) ? EOF : 0;
}

为什么这次转换无需进行强制类型转换,即可将*s分配给int,而不是先转换为unsigned char

3个回答

3
这并不是“符号扩展问题”。这个getchar实现确保所有成功读取的字符都作为非负整数int值返回。这个行为是getchar规范要求的,它明确表示读取的字符被转换为unsigned char值并转换成int值返回,即使char在给定平台上是有符号的。你看到的基本上是getchar规范的直接实现。
与此同时,fputs不返回任何特定的字符值。 fputs不会向用户返回c。那个c是纯粹的内部变量。它应该保留char类型在给定平台上的原始值,因为c的值随后会传递给putcputc不希望将字符值转换为非负范围,它希望使用原始字符值,如果char是有符号的,则很容易是负的。
顺便说一下,为什么你看的是fputs而不是fputc?如果你看fputc,就像getchar返回一个字符值一样,你可能会看到它在这方面的实现方式类似于getchar

为什么我看了fputs而不是fputc?书中没有给出fputc的示例。 - Derek
@Derek:好吧,说得也有道理。但是再次强调,fputs在这方面与getchar无法进行有意义的比较,因为它们的返回值规范完全不同。 - AnT stands with Russia

2
第一次我完全误解了问题。这里的问题在于,`getchar()`需要返回整个范围为0-255的`char`或`EOF`。在大多数平台上,`EOF=-1`。为了返回一个负值和一个字符,必须使用int。
而在`fputs`中并非如此。在这个例子中,while循环中将一个`char`赋值给了一个`int`。较低类型会提升为较高类型。来自KR C语言程序设计书第44页的引用:
如果任何一个操作数是long double,则另一个操作数将被转换为long double。 否则,如果任何一个操作数是double,则另一个操作数将被转换为double。 否则,如果任何一个操作数是float,则另一个操作数将被转换为float。 否则,将char和short转换为int 然后,如果任何一个操作数是long,则另一个操作数将被转换为long。

不,fputs 不接受 int 类型的参数。这是问题的前提。 - Potatoswatter
@Potatoswatter,你的意思是 putc 接受一个 int 类型参数。当无符号操作不发生时,低类型会提升为高类型。在 while 循环中此处不需要强制转换。请参见 KR 的第二版《C 程序设计语言》第44页。 - Freddie
是的,那就是我的意思。另一个前提是这种推广不一定做正确的事情;例如,负整数并不代表字符代码。这就是在 getchar 中进行显式转换的原因。另一方面,putc 被定义为接受不是字符代码的整数值,这很令人困惑(但只有非常敏锐的程序员才会注意到,甚至为此这个家伙应该得到点赞)。 - Potatoswatter

1
根据手册,
fputc()函数将字符c(转换为unsigned char)写入由stream指向的输出流。
该强制转换在函数内部专门执行。
除此之外,从char赋值给负int再返回char保证产生正确结果,而char到负int再到unsigned char保证与直接从char到unsigned char进行的强制转换具有相同的结果。其他情况可能会产生有符号整数溢出,这会产生未定义的行为(即可能崩溃)。但是大多数平台通过安静的二进制截断来处理这种情况,以使许多程序员根本不必担心它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接