我查找的所有内容都只是告诉我如何在 C 中进行补码运算/计算。
我想知道 C 在内部使用什么表示以及它如何处理溢出。
C 允许有符号整数的 3 种表示形式(https://port70.net/~nsz/c/c11/n1570.html#6.2.6.2p2):
- 符号位为 0 的对应值取反(符号和幅度);
- 符号位的值为 -(2M)(二进制补码);
- 符号位的值为 -(2M- 1) (反码)。
二进制补码是最常见的。
无符号溢出环绕无符号的最大值。
有符号溢出会导致未定义的行为。即,假设它不会发生,并且如果您确实发生了,则无法保证您的程序的行为。
签名原子中的溢出是一个例外:它定义明确,并且在那里强制要求补码:https ://port70.net/~nsz/c/c11/n1570.html#7.17.7.5p3 。
C 是否对负整数使用一个补码、二进制补码或符号/大小表示是实现定义的。也就是说,每个编译器都可以选择,通常基于它为其生成代码的处理器。
所以当你写
x = -x
编译器可能会生成相当于
x = ~x; /* one's complement */
或者
x = ~x + 1; /* two's complement */
或者
x ^= 0x80000000; /* sign/magnitude, assuming 32 bits */
当然,大多数时候您不必担心这一点。(而且大多数时候——现在可以肯定地说,一直都是——你在使用二进制补码的机器上工作,因为它是最受欢迎的。)
由于它是定义的实现,文档应该告诉你。但我想你总是可以用一段代码凭经验确定它:
#include <stdio.h>
#include <limits.h>
int main()
{
int x = 1;
int negativex = -x;
if(negativex == ~x)
printf("one's complement\n");
else if(negativex == ~x + 1)
printf("two's complement\n");
else if(negativex == (x ^ (1 << (sizeof(x) * CHAR_BIT - 1))))
printf("sign/magnitude\n");
else
printf("what the heck kind of machine are you on??\n");
}
你问过溢出。对于无符号整数,溢出以明显的方式定义为“环绕”(即,它以 2^N 为模执行,其中 N 是位数)。但是对于有符号整数,溢出在形式上是未定义的:理论上可能存在有符号整数溢出产生错误的机器,或多或少类似于除以 0。
(当然,在普通的二进制补码机器上,有符号整数算术也以明显的方式悄悄地环绕,因为二进制补码的全部意义在于环绕溢出使其工作。)
简而言之,我们可以说 C 中的2s 补码被定义为 C 中的 1 的补码与 1 之和。
C 中的2s 补码是从 C 中的 1s 补码生成的。众所周知,二进制数的 1s 补码是通过将位 1 转换为 0 和 0 转换为 1 来创建的。二进制数的 2s 补码是通过将二进制数的 1s 补码加 1 生成的。