我有一个简单的程序:
#include#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)
有谁可以指出这个问题?
这非常微妙.
程序中的每个整数文字都有一个类型.它具有哪种类型由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
如果它不适合,请尝试 long
如果它不适合,请尝试long long
.
Hex文字表现不同!如果文字不能适合签名类型int
,它将首先尝试unsigned int
继续尝试更大的类型.请参阅上表中的差异.
所以在32位系统上,你的文字0x80000000
是类型的unsigned int
.
这意味着您可以-
在文字上应用一元运算符而不调用实现定义的行为,就像溢出有符号整数时一样.相反,你将获得价值0x80000000
,一个正值.
bal < INT32_MIN
调用通常的算术转换,表达式的结果0x80000000
从中unsigned int
提升long long
.该值0x80000000
保留,0小于0x80000000,因此结果.
当您2147483648L
使用十进制表示法替换文字时,编译器不会选择unsigned int
,而是尝试将其放入a中long
.此外,L后缀表示long
如果可能,您需要一个.如果你继续阅读6.4.4.1中提到的表,L后缀实际上有类似的规则:如果数字不适合所请求的内容long
,而不是32位的话,编译器会给你一个long long
它的位置很合适.
0x80000000
是一个unsigned
值为2147483648 的文字.
对此使用一元减号仍会为您提供一个非零值的无符号类型.(事实上,对于非零值x
,您最终得到的值是UINT_MAX - x + 1
.)
这个整数文字0x80000000
有类型unsigned int
.
根据C标准(6.4.4.1整数常数)
5整数常量的类型是相应列表中可以表示其值的第一个.
并且这个整数常量可以用类型表示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
转换长长整型根据通常的算术转换的规则输入.
很明显,0小于0x80000000
.
数字常量0x80000000
是类型unsigned int
.如果我们采取-0x80000000
并做2s恭维数学,我们得到这个:
~0x80000000 = 0x7FFFFFFF 0x7FFFFFFF + 1 = 0x80000000
所以-0x80000000 == 0x80000000
.并且比较(0 < 0x80000000)
(因为0x80000000
是无符号的)是真的.
在思考-
数字常量的一部分时会出现混淆.
在下面的代码0x80000000
是数字常量.它的类型仅在此确定.将-
被应用之后并不会改变类型.
#define INT32_MIN (-0x80000000) long long bal = 0; if (bal < INT32_MIN )
原始的未经修饰的数字常数是正面的.
如果是小数,然后被分配类型是将其持有第一类:int
,long
,long long
.
如果常数是八进制或十六进制,它会保持它的第一种类型:int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.
0x80000000
,在OP的系统上得到unsigned
或的类型unsigned long
.无论哪种方式,它都是一些无符号类型.
-0x80000000
也是一些非零值并且是一些无符号类型,它大于0.当代码将其与a 进行比较时,比较的两侧long long
的值不会改变,所以0 < INT32_MIN
是真的.
另一种定义避免了这种奇怪的行为
#define INT32_MIN (-2147483647 - 1)
让我们走在梦幻之地了一段时间,其中int
和unsigned
48位.
然后0x80000000
适合,int
所以类型int
. -0x80000000
然后是负数,打印输出的结果是不同的.
[回到真实的话]
由于0x80000000
在签名类型之前适合某些无符号类型,因为它比some_signed_MAX
内部大some_unsigned_MAX
,所以它是一些无符号类型.
C有一个规则,即整数文字可能是signed
或unsigned
取决于它是否适合signed
或unsigned
(整数提升).在一32
台机器上,文字0x80000000
将是unsigned
.2的补充-0x80000000
是0x80000000
在32位机器上.因此,比较bal < INT32_MIN
是在比较之间signed
和unsigned
之前,根据C规则unsigned int
将转换为long long
.
[...]否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数将转换为操作数的类型.有符号整数类型.
因此,bal < INT32_MIN
永远true
.