注意:此答案不适用于许多编译器仍在使用的过时 ISO C90 标准
首先,在C99、C11上,-(-2147483648) == -2147483648这个表达式其实是false:
int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);
打印
0
那么这怎么可能计算为真呢?
机器使用 32 位 two's complement 整数。 2147483648 是一个完全不适合 32 位的整数常量,因此它将是 long int 或 long long int 取决于它适合的第一个位置。这个否定将导致-2147483648 - 再次,即使数字-2147483648 可以适合32 位整数,表达式-2147483648 包含一个> 32 位正整数,前面是一元-!
您可以尝试以下程序:
#include <stdio.h>
int main() {
printf("%zu\n", sizeof(2147483647));
printf("%zu\n", sizeof(2147483648));
printf("%zu\n", sizeof(-2147483648));
}
这种机器上的输出很可能是 4、8 和 8。
现在,-2147483648 negated 将再次导致 +214783648,它仍然是 long int 或 long long int 类型,一切都很好。
在 C99、C11 中,整型常量表达式 -(-2147483648) 在所有符合要求的实现上都有很好的定义。
现在,当将此值分配给具有 32 位和二进制补码表示的 int 类型的变量时,该值无法在其中表示 - 32 位 2 的补码上的值范围从 -2147483648 到 2147483647 .
C11 标准6.3.1.3p3 说明了以下整数转换:
- [当]新类型有符号且值不能在其中表示时;结果要么是implementation-defined,要么是一个implementation-defined信号。
也就是说,C 标准实际上并没有定义这种情况下的值是什么,或者不排除程序执行由于发出信号而停止的可能性,而是将其留给实现(即编译器)来决定如何处理它(C11 3.4.1):
实现定义的行为
每个实现都记录了如何做出选择的未指定行为
和(3.19.1):
实现定义的值
每个实现记录如何做出选择的未指定值
在您的情况下,实现定义的行为是该值是 32 个最低位 [*]。由于 2 的补码,(long)long int 值0x80000000 设置了第 31 位,并清除了所有其他位。在 32 位二进制补码整数中,第 31 位是符号位 - 表示该数字为负数;所有值位为零表示该值是最小可表示数字,即INT_MIN。
[*] GCC documents its implementation-defined behaviour in this case as follows:
当值不能在该类型的对象中表示时,将整数转换为有符号整数类型(C90 6.2.1.2、C99 和 C11 6.3.1.3)的结果或引发的信号。
为了转换为宽度N的类型,该值以2^N为模减少到该类型的范围内;没有发出信号。