我下面有一个简单的程序:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
{
long long bal = 0;
if(bal < INT32_MIN )
{
printf("Failed!!!");
}
else
{
printf("Success!!!");
}
return 0;
}
条件if(bal < INT32_MIN)总是为真。这怎么可能?
如果我将宏更改为:
#define INT32_MIN (-2147483648L)
有人能指出这个问题吗?
这个整数字面值0x80000000的类型是unsigned int。
根据C标准(6.4.4.1整数常量)
整数常量的类型是对应的第一个
可以表示其值的列表。
这个整数常量可以用unsigned int类型表示。
这个表达式
-0x80000000具有相同的unsigned int类型。此外,它具有相同的价值
0x80000000在两个补式表示中,以以下方式计算
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
这有一个副作用,如果写
int x = INT_MIN;
x = abs( x );
结果仍然是INT_MIN。
因此在这种情况下
bal < INT32_MIN
根据通常的算术转换规则,将0与无符号值0x80000000转换为long long int类型进行比较。
显然,0小于0x80000000。
这是相当微妙的。
程序中的每个整型字面值都有一个类型。它的类型由6.4.4.1中的表规定:
Suffix Decimal Constant Octal or Hexadecimal Constant
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
如果一个字面值无法放入默认int类型,它将尝试上表中所示的下一个更大的类型。对于常规的十进制整数字面量,它是这样的:
尝试int
如果不合身,试试长一点
如果装不下,就试试长长。
但十六进制文字的表现不同!如果字面量不适合int这样的有符号类型,它将首先尝试unsigned int,然后再尝试更大的类型。请参见上表中的差异。
所以在32位系统中,你的文字0x80000000是unsigned int类型。
这意味着可以对文字应用一元运算符,而无需调用实现定义的行为,就像溢出有符号整数时那样。相反,您将得到值0x80000000,这是一个正值。
bal < INT32_MIN调用通常的算术转换,表达式0x80000000的结果从unsigned int提升为long long。值0x80000000被保留,并且0小于0x80000000,因此得到这样的结果。
当你用2147483648L替换字面量时,你使用的是十进制计数法,因此编译器不会选择unsigned int,而是尝试将其放入long。此外,L后缀表示如果可能的话,你想要长一点。如果你继续阅读6.4.4.1中提到的表格,L后缀实际上也有类似的规则:如果数字不适合所请求的long,在32位的情况下是不适合的,编译器会给你一个long long,它会很好地适合。
这个整数字面值0x80000000的类型是unsigned int。
根据C标准(6.4.4.1整数常量)
整数常量的类型是对应的第一个
可以表示其值的列表。
这个整数常量可以用unsigned int类型表示。
这个表达式
-0x80000000具有相同的unsigned int类型。此外,它具有相同的价值
0x80000000在两个补式表示中,以以下方式计算
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
这有一个副作用,如果写
int x = INT_MIN;
x = abs( x );
结果仍然是INT_MIN。
因此在这种情况下
bal < INT32_MIN
根据通常的算术转换规则,将0与无符号值0x80000000转换为long long int类型进行比较。
显然,0小于0x80000000。