C中的按位移位
我得到了一些令我困惑的C代码:
int a = 1; int b = 32; printf("%d\n %d\n", a<<b, 1<<32);
输出是
1 0
代码在Ubuntu 16.04(Xenial Xerus)上运行,我使用GCC版本5.4.0的gcc -m32 ac
编译它。
我读过一些post已经解释了为什么a<<b
输出1,但我不明白为什么1<<32
结果为0.我的意思是, a<<b
和1<<32
之间a<<b
什么区别?
将32位int
移位32是未定义的行为,因此可以生成任何值作为结果。 在1<<32
表达式的情况下,您的C编译器应该警告您。
您看到的两个输出不同的原因是它们由不同的代码路径生成:
-
a << b
使用变量,因此它在运行时由编译的代码计算 -
1<<32
是一个常量表达式,因此它在编译时由编译器本身计算
看起来编译的代码按模32执行移位,因此移位32与移位零相同。 然而,编译器本身移位32,将一位丢弃。 编译器可以自由地执行此操作,因为此行为未定义。 因此,该标准不要求任何特定行为,甚至同一程序的各部分之间的一致行为。
a<和
1<<32
是未定义的行为 ,因为右操作数等于位数。
C11§6.5.7按位移位算子
第3段:
对每个操作数执行整数提升。 结果的类型是提升的左操作数的类型。 如果右操作数为负数,或者大于或等于左表达式类型中的位数,则结果为undefined。
第4段:
E1 << E2
的结果是E1
左移E2
位位置; 腾出的位用零填充。 如果E1
具有无符号类型,则结果的值为E1 × 2E2
,模数比结果类型中可表示的最大值减1。 如果E1
具有有符号类型和非负值,并且在结果类型中可以表示E1 × 2E2
,那么这就是结果值; 否则, 行为未定义。
因此,如果数字的移位大于整数的大小,则行为是未定义的。
GCC产生警告:
warning: left shift count >= width of type [-Wshift-count-overflow] printf("%d\n%d",a<
通过恒定值移位而不是移位b位可以导致在未定义的行为可能优化不存在的规则下的不同行为。 换句话说,移位恒定值可能与移位可变位数的方式不同。 两者都显然是未定义的行为,因此编译器可以随心所欲地处理任何一种情况。 它可以从字面上生成随机数。
转移签名值是危险的,应该避免。
如果int的大小是32位,则1 << 31是依赖于实现的 - 二的补码= -2147483648。 -1 << 31给出相同的结果。
如果将值> =次移位,则结果未定义的位数。