简答:英特尔处理器将移位计数屏蔽为 5 位(最多 31 位)。也就是说,实际执行的移位是32&31,也就是0(没有变化)。
在 Linux 32 位 PC 上使用 gcc 会出现相同的结果。
我组装了这个程序的一个更短的版本,因为我很困惑为什么 32 位的左移会导致一个非零值:
int main(){
int y = 32;
unsigned int z = 1 << y;
unsigned int k = 1;
k <<= y;
printf("z: %u, k: %u\n", z, k);
}
..使用命令gcc -Wall -o a.s -S deleteme.c(cmets是我自己的)
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $32, -16(%ebp) ; y = 32
movl -16(%ebp), %ecx ; 32 in CX register
movl $1, %eax ; AX = 1
sall %cl, %eax ; AX <<= 32(32)
movl %eax, -12(%ebp) ; z = AX
movl $1, -8(%ebp) ; k = 1
movl -16(%ebp), %ecx ; CX = y = 32
sall %cl, -8(%ebp) ; k <<= CX(32)
movl -8(%ebp), %eax ; AX = k
movl %eax, 8(%esp)
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
好的,这是什么意思?正是这条指令让我感到困惑:
sall %cl, -8(%ebp) ; k <<= CX(32)
显然 k 正在左移 32 位。
你找到了我 - 它使用的是 sall 指令,它是 arithmetic shift。我不知道为什么将其旋转 32 会导致该位重新出现在初始位置。我最初的猜想是处理器被优化为在一个时钟周期内执行这条指令——这意味着任何超过 31 的移位都将被视为无关紧要。但我很想找到这个问题的答案,因为我希望旋转应该导致所有位从数据类型的左端脱落。
我找到了一个指向http://faydoc.tripod.com/cpu/sal.htm 的链接,它解释了移位计数(在 CL 寄存器中)被屏蔽为 5 位。这意味着如果您尝试移动 32 位,则执行的实际移动将是零位(即没有变化)。答案来了!