无法理解C代码中的移位操作符行为

时间:2012-01-30 20:59:50

标签: c bit-shift

查看此示例C代码(以测试用例的形式提取):

main() {
  unsigned long a, b;
  int c;
  c = 32;
  a = 0xffffffff << 32;
  b = 0xffffffff << c;
  printf ("a=%x, b=%x\n", a, b);
}

打印:a=0, b=ffffffff

我无法理解为什么b不是零,就像一个。我在Microsoft C和GCC上测试了这个。

更新:我修复了愚蠢的错字(应该是&lt;&lt; c而不是&lt;&lt; b当然)。但我的问题仍然存在,例如结果仍然相同。

9 个答案:

答案 0 :(得分:6)

在此处使用之前,您从未将b初始化为任何内容:

b = 0xffffffff << b;

所以它可以是任何东西。 (我认为你实际上是想转移c。)


除此之外:

主要问题是,按数据类型或更多位的#位移动是未定义的行为
因此,如果文字0xffffffff是32位整数,则行:

a = 0xffffffff << 32;

未由标准定义。 (见评论。)


您还应该收到编译器警告:

warning C4293: '<<' : shift count negative or too big, undefined behavior

答案 1 :(得分:3)

在C中,左移位超过提升操作数的宽度是未定义的行为。

  

(C99,6.5.7p3)“如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。”

我假设在下面的示例中intunsigned int类型是32位。

未填充的十六进制整数常量的类型是相应列表中第一个可以表示其值的类型:intunsigned intlongunsigned longlong longunsigned long long

所以0xFFFFFFFF类型为unsigned int0xFFFFFFFF << 32是未定义的行为。

答案 2 :(得分:2)

我敢打赌,在修正你的拼写错误后,你会得到2个零。原因如下: 如果您的程序(我打赌是)编译为发布版,则您已启用优化。这意味着编译器将使用所谓的“常量折叠”,CPU甚至不会执行这些转换。在引擎盖下代码将有2个零常量压入堆栈并调用printf。这些操作(转换)的结果将基本上成为程序中的常量。所以没有未定义的行为等等 - 你的a和b变成了常数值,你会得到像:

push 0
push 0
push offset to printf format string
call to printf

答案 3 :(得分:2)

没有人提到问题的另一个有趣的方面。在x86计算机上,移位量使用模数32,因此移位32实际上是移位0.在ARM机器上,移位值(如果来自寄存器)不会改变,因此任何移位值为32或更大将始终在目标寄存器中产生0。

特别是在您的问题中,编译器对常量值很聪明,因此它将(0xffffffff&lt;&lt; 32)转换为正确的值(0)。在第二个示例中,编译器无法直接计算移位量,因为它来自变量。在Intel机器上执行时,向左移动32会导致左移0。

答案 4 :(得分:1)

您有b = 0xffffffff << b;,但也许您的意思是b = 0xffffffff << c;

答案 5 :(得分:0)

为什么它应该为零?您可以将其转换为可以为0的垃圾编号(b的第一个值)。

答案 6 :(得分:0)

你没有初始化B,因此它会在程序运行时在该特定内存位置具有任何(随机)值。

答案 7 :(得分:0)

当您到达b = 0xffffffff << b;行时,尚未分配b的值。看起来它的值为零,制作指令b = 0xffffffff << b;。这会给出给定的答案。

答案 8 :(得分:0)

良好的做法是转移后的和。在这种情况下,你知道发生了什么事。

Examply:

b << = 4;
b &= 0xfffffff0;