"the C programming language" 本书习题1.2可能的错误结果

"the C programming language" book exercise 1.2 possible wrong result

#include <stdio.h>
   /* print Fahrenheit-Celsius table
       for fahr = 0, 20, ..., 300 */
main()
   {
     int fahr, celsius;
     int lower, upper, step;
     lower = 0;
     upper = 300;
     step = 20;
     fahr = lower;
     while (fahr <= upper) {
         celsius = 5 * (fahr-32) / 9;
         printf("%d\t%d\n", fahr, celsius);
         fahr = fahr + step;
    } 
}

根据上面的代码,我不明白你是如何在第一行得到下面的结果的。不应该是0 -17吗?此示例位于 C 编程语言的第 17 页。我只是想确保这与 C 的旧实现无关,并且我没有得到与本书相同的结果,因为我使用的是更新的编译器。

1    -17
20   -6
40   4
60   15
80   26
100  37
120  48
140  60
160  71
180  82
200  93
220  104
240  115
260  126
280  137
300  148

确实,在第一次通过 while 循环时,fahr 的值为 0,这就是将要打印的内容。这可能是您的本书版本中的错字:我们在评论中有多个报告说其他版本有 0 而您的版本有 1。

您担心旧的 C 代码在新的编译器中表现不同是正确的。然而,这个程序中没有任何通常会导致较新编译器出现问题的东西:没有指针别名、没有并发、没有算术溢出等。 celsius 列的前两个值的计算确实涉及带负红利的整数除法,这是在 C1999 之前部分实现定义的;如果书中为这些值给出了 -18-7,那就是原因。现代编译器会发出有关缺少 return 类型 main 的警告,但这不会影响程序的语义。 (我不知道 any 现代 C 编译器实际上为 "implicit int" 发出 error它的 "strictly conforming to C2011" 模式。当然,通常有一种方法可以告诉它将该特定警告变成错误。)这些问题中的 None 可能导致 fahr 的第一个值变为打印为 1。

请从中吸取教训,您应该多相信自己在心理上模拟程序执行的能力,而少相信印刷书籍。还建议您获得更新的 C 教科书——不仅仅是更新版本的 K&R,而是一本涵盖现代语言的全新书籍。不过很遗憾,我没有任何建议。

K&R 是恐龙在地球上行走时编写的。在预标准 C 和 C90 上,负整数除法的舍入方向没有明确定义。因此,根据编译器的不同,您可能会得到不同的结果,这在过去是实现定义的行为。我不确定 K&R 是否意识到了这个问题——这本书充满了对不明确行为的依赖。

C 语言中的这个错误直到 1999 年才被修复,这本书写成大约 20 年后。


参见What is the behavior of integer division?,特别是schot的回答。

另见 C99 基本原理 6.5.5

In C89, division of integers involving negative operands could round upward or downward in an implementation-defined manner; the intent was to avoid incurring overhead in run-time code to check for special cases and enforce specific behavior. In Fortran, however, the result will always truncate toward zero, and the overhead seems to be acceptable to the numeric programming community. Therefore, C99 now requires similar behavior, which should facilitate porting of code from Fortran to C. The table in §7.20.6.2 of this document illustrates the required semantics.