在x86_64平台上,下面这段代码输出的结果是否相同?
#include <stdint.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
uint32_t k1 = 57;
const uint64_t one = 1;
uint64_t r0 = one << k1;
uint64_t r1 = 1 << k1;
printf("r0 = %lu, r1 = %lu\n", r0, r1);
return 0;
}
。。。。。。
输出结果:
r0 = 144115188075855872, r1 = 33554432
原因:
相关汇编代码
0000000000400530 <main>:
400530: 55 push %rbp
400531: 48 89 e5 mov %rsp,%rbp
400534: 48 83 ec 30 sub $0x30,%rsp
400538: 89 7d dc mov %edi,-0x24(%rbp)
40053b: 48 89 75 d0 mov %rsi,-0x30(%rbp)
40053f: c7 45 fc 39 00 00 00 movl $0x39,-0x4(%rbp)
400546: 48 c7 45 f0 01 00 00 movq $0x1,-0x10(%rbp)
40054d: 00
40054e: 8b 45 fc mov -0x4(%rbp),%eax
400551: 48 8b 55 f0 mov -0x10(%rbp),%rdx
400555: 89 c1 mov %eax,%ecx
400557: 48 d3 e2 shl %cl,%rdx
40055a: 48 89 d0 mov %rdx,%rax
40055d: 48 89 45 e8 mov %rax,-0x18(%rbp)
400561: 8b 45 fc mov -0x4(%rbp),%eax
400564: ba 01 00 00 00 mov $0x1,%edx
400569: 89 c1 mov %eax,%ecx
40056b: d3 e2 shl %cl,%edx
40056d: 89 d0 mov %edx,%eax
40056f: 48 98 cltq
400571: 48 89 45 e0 mov %rax,-0x20(%rbp)
400575: 48 8b 55 e0 mov -0x20(%rbp),%rdx
400579: 48 8b 45 e8 mov -0x18(%rbp),%rax
40057d: 48 89 c6 mov %rax,%rsi
400580: bf 30 06 40 00 mov $0x400630,%edi
400585: b8 00 00 00 00 mov $0x0,%eax
40058a: e8 81 fe ff ff callq 400410 <printf@plt>
从汇编代码上可以看到,对于第一条位移语句,使用的是寄存器%rdx(64位)保存位移结果;而第二条位移语句,使用寄存器%edx(32位)保存位移结果,然后再通过cltq指令,将结果扩展到64位。
因为对于32位的数位移57位会发生溢出
实际%edx保存值为((uint64_t)1 << 57) % ((2^32)-1) = 144115188075855872 % 4294967295 = 33554432
cltq R[%rax ] <- SignExtend(R[%eax]) Convert %eax to quad word,将$eax转化为8字,即%rax。
cltq是有符号数的扩展,如果%eax的最高的32位为1的话,刚%eax的高32扩展后全为1;相反如果%eax的高位为0的话,则扩展出来后全为0。