怎么用计算机组成原理的知识去解释这个现象

img

32位有符号整型数值范围是[-2147484648, 2147484647]。数据溢出,数据存储会以补码的形式存储。因此最高位变成了1,成为了一个复数。建议改为unsigned int,输出格式为%u就可显示正常

32位有符号整型数值范围是[-2147484648, 2147484647],a/-1结果应该是2147484648,但是2147484648超出了32位有符号整型的数值范围,属于溢出。C/C++语言标准里把有符号整型溢出划为未定义行为,其结果依赖编译器实现。

From https://en.cppreference.com/w/c/language/operator_arithmetic#Overflows

When signed integer arithmetic operation overflows (the result does not fit in the result type), the behavior is undefined: it may wrap around according to the rules of the representation (typically 2's complement), it may trap on some platforms or due to compiler options (e.g. -ftrapv in GCC and Clang), or may be completely optimized out by the compiler.

下面只给出gcc编译器对这种溢出的结果解释

源码

#include <stdio.h>

int main()
{
    int a = 0x80000000;
    int b = a / -1;
    printf("%d\n", b);
}

汇编代码

    .file    "main.c"
    .text
    .section    .rodata
.LC0:
    .string    "%d\n"
    .text
    .globl    main
    .type    main, @function
main:
.LFB0:
    .cfi_startproc
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $-2147483648, -4(%rbp)   // a = 0x80000000
    movl    -4(%rbp), %eax
    negl    %eax                     // 计算a/-1
    movl    %eax, -8(%rbp)           // 结果赋给b
    movl    -8(%rbp), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size    main, .-main
    .ident    "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
    .section    .note.GNU-stack,"",@progbits

由上面代码可以看出a/-1是由negl指令完成计算的
negl指令相当于取反加一 (https://www.aldeid.com/wiki/X86-assembly/Instructions/neg)
对0x80000000取反加一结果还是0x80000000

not(0x80000000) => 0x7FFFFFFF
not(0x80000000) + 1 => 0x80000000

整型最高位是符号位,所以最终输出结果是-2147483648