首页 > 解决方案 > 将无符号整数(通过否定它)分配给有符号长长时的意外值

问题描述

我碰巧遇到了一个奇怪的问题,请参见下面的代码:

#include <stdio.h>

int main() {
    unsigned char a = 10;
    unsigned int b = 10;

    long long x = -a;
    long long y = -b;

    printf("x = %lld, y = %lld\n", x, y);
    return 0;
}

输出:

x = -10, y = 4294967286

如您所见,当我分配-by(long long) 时,它给出了错误的结果,但 x 的值符合预期。

在阅读了asm代码后,我发现当将负无符号字符分配给long long时,编译器会生成一条cdqe指令(见+25行)将符号扩展为rax,而当unsigned int为long时它不会做同样的事情长。

我知道我们得到值的原因4294967286是 rax 的高 32 位全为零,rax = 0x00000000fffffff6.

cdqe所以我的问题是为什么编译器在 unsigned int 情况下缺少指令?

Dump of assembler code for function main:
   0x000000000040052d <+0>:     push   rbp
   0x000000000040052e <+1>:     mov    rbp,rsp
   0x0000000000400531 <+4>:     sub    rsp,0x20
=> 0x0000000000400535 <+8>:     mov    BYTE PTR [rbp-0x1],0xa
   0x0000000000400539 <+12>:    mov    DWORD PTR [rbp-0x8],0xa
   0x0000000000400540 <+19>:    movzx  eax,BYTE PTR [rbp-0x1]
   0x0000000000400544 <+23>:    neg    eax
   0x0000000000400546 <+25>:    cdqe   
   0x0000000000400548 <+27>:    mov    QWORD PTR [rbp-0x10],rax
   0x000000000040054c <+31>:    mov    eax,DWORD PTR [rbp-0x8]
   0x000000000040054f <+34>:    neg    eax
   0x0000000000400551 <+36>:    mov    eax,eax
   0x0000000000400553 <+38>:    mov    QWORD PTR [rbp-0x18],rax
   0x0000000000400557 <+42>:    mov    rdx,QWORD PTR [rbp-0x18]
   0x000000000040055b <+46>:    mov    rax,QWORD PTR [rbp-0x10]
   0x000000000040055f <+50>:    mov    rsi,rax
   0x0000000000400562 <+53>:    mov    edi,0x400610
   0x0000000000400567 <+58>:    mov    eax,0x0
   0x000000000040056c <+63>:    call   0x400410 <printf@plt>
   0x0000000000400571 <+68>:    mov    eax,0x0
   0x0000000000400576 <+73>:    leave  
   0x0000000000400577 <+74>:    ret    

环境:

OS: CentOS Linux release 7.8.2003 (Core), Linux 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 GNU/Linux
Gcc: gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

标签: cassembly

解决方案


这与隐式整数转换有关。

  • :unsigned char a首先转换为 anint然后取反。结果,-10

  • :没有unsigned int b被转换,所以否定了你会得到.-bunsigned intUINT_MAX - 10 + 1

如果您先转换为目标类型-(long long)b,您也会到达-10那里。

进一步阅读:隐式转换


推荐阅读