关于C移位操作

在x86_64平台上,下面这段代码输出的结果是否相同?

#include 
#include 

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 
: 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

从汇编代码上可以看到,对于第一条位移语句,使用的是寄存器%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。

你可能感兴趣的:(Linux)