C语言中,将TMin(32位有符号整数的最小值)写成 -2147483647-1,而不是简单地写成 -2147483648 或 0x80000000 。这是为什么呢?
首先,对于整数常量的定义:
An integer constant begins with a digit, but has no period or exponent part. It may have a prefix that specifies its base and a suffix that specifies its type.
可见如果不发生溢出,整型常量的值总是非负数。如果前面出现符号,则是对整型常量使用的一元运算符,而不是整型常量的一部分。整型常量的实际类型取决于长度、基数、后缀字母和C语言实现确定的类型表示精度。具体的根据表一来确定。
其次,看看C语言中limits.h中对于INT_MIN和INT_MAX的定义如下所示:
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
那么考虑到以上两点,如果将TMin32写成-2147483648并且将代码在一个32位的机器上面编译,当编译器遇到-X形式的数值,它首先会确定X的数据类型和值,然后取X的。
ISO C90 | ISO C99 | ||
十进制(Decimal) | 十六进制(Hexadecimal) | 十进制(Decimal) | 十六进制(Hexadecimal) |
int
long
unsigned
unsigned long
|
int
unsigned
long
unsigned long
|
int
long
long long
|
int
unsigned
long
unsigned long
long long
unsigned long long
|
表一 根据C语言版本和常量的格式(十进制和十六进制),常量的数据类型是从上面表格里选择第一个合适(能表示常量)的类型。
Word Size | ISO C90 | ISO C99 | ||
Expression | -2147483648 | 0x80000000 | -2147483648 | 0x80000000 |
32
64
|
unsigned
long
|
unsigned
unsigned
|
long long
long
|
unsigned
unsigned
|
表二 根据C语言版本和常量的格式(十进制和十六进制),可以为这两个表达式得到三种不同的数据类型,包括数据为正值的情况。
由于2147483648超过了int所能够表示的范围,编译器就会再次选择一种可以正确的表示此值的类型。然后它就会按照第一个表格(十进制)的顺序往下继续尝试类型,再假设编译器采用的标准是ISO C90,int->long->unsigned,然后就发现unsigned是第一个合适的数据类型。正如我们知道的,2147483648和-2147483648在32位数值上拥有同样的位表示,使得此常量的最终数据类型是unsigned且值为2147483648。这对于16进制的0x80000000也是相同的结果。
对于64位来说十进制和十六进制会分别选择long(-2147483648)和unsigned(2147483648)。而ISO C99的情况则是按照上述规则数据类型为long long才能容纳2147483648。
最后结论是:-214748364832位机器里使用ISO C90进行编译,会先将2147483648看成是一个unsigned,再对2147483648进行无符号的加法逆元计算(见csapp第二版56页),得到结果是:
(-uw2147483648) = 2w - x = 232 - 231 = 231 = 2147483648。
所以对于下面的表达式:
int result = -2147483648 < 2147483647;
在32位机器里面使用ISO C90进行编译, result = 0。
参考资料:
cs: app web aside