为什么1ul<< 64返回1而不是0?

时间:2016-05-04 04:27:01

标签: c math gcc long-integer unsigned

考虑以下代码:

// 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,但在第二种情况下它是正确的?

1 个答案:

答案 0 :(得分:8)

根据§6.5.7/ 3,按类型或更大的宽度移动会导致undefined behaviour

  
      
  1. 对每个操作数执行整数提升。类型   结果是提升左操作数的结果。如果值   右操作数为负数或大于或等于宽度   提升的左操作数,行为未定义
  2.   

这个的基本原理是各种CPU为超大尺寸的移位实现不同的行为,并且在这种情况下定义行为的限制太多 - 许多移位需要额外的程序集生成。 (虽然它应该是实现定义的而不是未定义的。)

您的观察结果可以通过使用英特尔家族CPU来解释,在硬件中,64位类型的移位宽度为“mod 64”,因此在运行时1ul << 641ul << 0相同;但在编译时,在另一种情况下,编译器使用算术规则计算1ul << 64