考虑以下代码:
// Simply loop over until 64 is hit.
unsigned long x = 0;
for (int i = 0; i <= 64; i++) {
if (i == 64) {
x = 1ul << i;
printf("x: %d\n", x);
}
}
我们知道无符号长是64位宽,左移1到64位将变为1000 ... 000(一个后面有64个零),并且会被截断为0.但是,实际的打印输出给出:
x: 1
奇怪的是,如果我们做的话
printf("x: %d\n", (1ul << 64));
它会打印0.
任何人都可以解释为什么会这样吗?为什么在第一种情况下,程序错误地产生1而不是0,但在第二种情况下它是正确的?
答案 0 :(得分:8)
根据§6.5.7/ 3,按类型或更大的宽度移动会导致undefined behaviour:
- 对每个操作数执行整数提升。类型 结果是提升左操作数的结果。如果值 右操作数为负数或大于或等于宽度 提升的左操作数,行为未定义。
醇>
这个的基本原理是各种CPU为超大尺寸的移位实现不同的行为,并且在这种情况下定义行为的限制太多 - 许多移位需要额外的程序集生成。 (虽然它应该是实现定义的而不是未定义的。)
您的观察结果可以通过使用英特尔家族CPU来解释,在硬件中,64位类型的移位宽度为“mod 64”,因此在运行时1ul << 64
与1ul << 0
相同;但在编译时,在另一种情况下,编译器使用算术规则计算1ul << 64
。