使用gcc按位移动的意外行为

我有一个像这样的testing程序:

int main() { unsigned n = 32; printf("ans << 32 = 0x%X\n", (~0x0U) << 32); printf("ans >> 32 = 0x%X\n", (~0x0U) >> 32); printf("ans << n(32) = 0x%X\n", (~0x0U) << n); printf("ans >> n(32) = 0x%X\n", (~0x0U) >> n); return 0; } 

它产生以下输出:

 ans << 32 = 0x0 ... (1) ans >> 32 = 0x0 ... (2) ans << n(32) = 0xFFFFFFFF ... (3) ans >> n(32) = 0xFFFFFFFF ... (4) 

我期望(1)和(3)是一样的,以及(2)和(4)是一样的。

使用gcc版本:gcc.real(Ubuntu 4.4.1-4ubuntu9)4.4.1

发生什么事?

根据C标准 §6.5.7.3,按类型大小移动是未定义的行为:

6.5.7按位移位运算符
(…)如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为是未定义的。

你的编译器应该警告你:

 $ gcc shift.c -o shift -Wall shift.c: In function 'main': shift.c:5:5: warning: left shift count >= width of type [enabled by default] shift.c:6:5: warning: right shift count >= width of type [enabled by default] 

如果你看看gcc生成的汇编代码 ,你会发现它实际上是在编译时计算前两个结果。 简化:

 main: movl $0, %esi call printf movl $0, %esi call printf movl -4(%rbp), %ecx ; -4(%rbp) is n movl $-1, %esi sall %cl, %esi ; This ignores all but the 5 lowest bits of %cl/%ecx call printf movl -4(%rbp), %ecx movl $-1, %esi shrl %cl, %esi call printf