【发布时间】:2018-07-19 18:04:18
【问题描述】:
这里是部分源代码(示例代码来自《Cprimer plus》一书):
float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
预期的输出是:
0 1074266112 0 1074266112
书中明确解释了原因:
参数传递的机制取决于实现。这 是参数传递在一个系统上的工作方式。函数调用看起来 如下:
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
本次通话 告诉计算机交出变量 n1 , n2 的值, n3 和 n4 到计算机。这是一种常见的方式 完成。程序将值放在内存区域中 称为堆栈。当计算机将这些值放入堆栈时, 它由变量的类型指导,而不是由转换 说明符。因此,对于 n1 ,它将 8 个字节放在堆栈上( float 转换为 double )。同样,它又放置了 8 个字节用于 n2 ,后跟 n3 和 n4 各 4 个字节。然后控制转移到 printf() 函数。此函数从堆栈中读取值 但是,当它这样做时,它会根据转换读取它们 说明符。 %ld 说明符指示 printf() 应为 4 字节,所以 printf() 读取堆栈中的前 4 个字节作为它的第一个字节 价值。这只是 n1 的前半部分,它被解释为 长整数。下一个 %ld 说明符再读取 4 个字节;这只是 n1 的后半部分并被解释为第二个长整数(参见 图 4.9)。同样,%ld 的第三个和第四个实例导致 n2 的前半部分和后半部分被读取并被解释为 还有两个长整数,所以虽然我们有正确的说明符 n3 和 n4 , printf() 正在读取错误的字节。
我可以得到书上所说的内容,并且我自己的 PC 用于每种数据类型的内存与上述相同。
但是当我自己编译和运行代码时,我得到以下输出:2000000000 1234567890 2147483626 0
我的开发环境是:Ubuntu16.04LTS,gcc 5.4.0,C11 标准。
我不知道是什么原因导致我自己的输出与预期的不同。
【问题讨论】:
-
这是未定义的行为,简单明了。
-
可能您在 x64 系统上,
long在整数寄存器中传递,double在浮点寄存器中传递 -
它高度依赖于您的特定编译器。您使用的是与本书不同的编译器,因此得到了不同的结果。
-
这完全取决于平台,我得到了 2000000000 1234567890 4195840 140523256637664 here
-
人们一直说这取决于您的平台,这是真的;然而这是explicitly undefined behavior:“如果任何参数不是相应转换规范的正确类型,则行为未定义。”任何事情都可能发生,包括根据月相在同一平台上的不同行为。