【问题标题】:unexpected value when assign unsigned int(by negtive it) to signed long long [duplicate]将无符号整数(通过否定它)分配给有符号长长时的意外值[重复]
【发布时间】:2021-09-11 13:09:27
【问题描述】:

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

#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

如您所见,当我将-b 分配给y(long long)时,它给出了错误的结果,但x 的值与预期的一样。

在阅读asm代码后,我发现当将负无符号字符赋值给long long时,编译器会生成一个cdqe指令(见+25行)将符号扩展为rax,但它并没有这样做unsigned int 到 long long 时的事情。

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

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

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)

【问题讨论】:

  • 4294967286 是 -10 作为无符号整数。它正在做你告诉它做的事情。
  • 是不是因为它产生的结果是二进制补码?
  • @Lundin Wow ...这是一个非常彻底的答案。 :-)
  • @Sorenp 无符号算术转换不依赖于 2 的补码。标准说Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.这是数学值。因此,无论系统上使用何种签名格式,您都会得到数学结果 -10 + UINT_MAX + 1。

标签: c assembly


【解决方案1】:

这与隐式整数转换有关。

  • unsigned chara:首先转换为int,然后取反。结果,-10

  • unsigned intb没有转换,所以-b 否定unsigned int,你会得到UINT_MAX - 10 + 1

如果您先转换为目标类型-(long long)b,您也会在那里获得-10

延伸阅读:Implicit conversions

【讨论】:

  • 感谢您的简要说明。我之前似乎误解了类型类型提升。我以为所有的右手操作数都会被转换为右手操作数的类型,然后进行算术和赋值。
  • @leen 不客气!隐式转换有点乱 imo
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
  • 2012-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多