为什么是0 <-0x80000000?
我有一个简单的程序下面:
#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 )
始终为真的条件。 这怎么可能?
它工作正常,如果我改变macros:
#define INT32_MIN (-2147483648L)
任何人都可以指出这个问题?
这很微妙。
程序中的每个整数字面都有一个types。 它具有哪种types由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
types中,它将尝试下一个更大的types,如上表所示。 所以对于常规的十进制整数文字,它是这样的:
- 尝试
int
- 如果不适合,请尝试一下
- 如果不适合,请尝试
long long
。
尽pipeHex文字的行为不同! 如果文字不能像int
这样的有符号types,它将首先尝试unsigned int
然后继续尝试更大的types。 查看上表中的区别。
所以在32位系统上,你的文字0x80000000
是unsigned int
types的。
这意味着您可以将一元运算符应用于文字,而不用调用实现定义的行为,就像在溢出有符号整数时一样。 相反,您将得到值0x80000000
,一个正值。
bal < INT32_MIN
调用通常的算术转换,expression式0x80000000
的结果从unsigned int
提升为long long
。 值0x80000000
被保留,0小于0x80000000,因此结果。
当你用2147483648L
代替文字时,你使用十进制符号,因此编译器不会selectunsigned int
,而是试图把它放在一个long
。 另外L后缀说, 如果可能的话,你想要一个long
。 L后缀实际上有相似的规则,如果你继续阅读6.4.4.1中提到的表格:如果数字不符合要求的long
,而不是在32位的情况下,编译器会给你一个long long
地方,它会适合就好。
0x80000000
是一个值为2147483648的unsigned
文字。
对此应用一元减法仍然会给你一个非零值的无符号types。 (事实上,对于非零值x
,最终的值是UINT_MAX - x + 1
)
这个整型文字0x80000000
types是unsigned int
。
根据C标准(6.4.4.1整型常量)
5一个整数常量的types是其中可以表示其值的对应列表的第一个。
而这个整数常量可以用unsigned int
的types表示。
所以这个expression
-0x80000000
具有相同的unsigned int
types。 而且它在二进制补码表示中具有相同的值0x80000000
,计算方式如下
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
例如,如果写的话会有副作用
int x = INT_MIN; x = abs( x );
结果将再次INT_MIN
。
因此在这种情况下
bal < INT32_MIN
根据通常的算术转换的规则,将0
与无符号值0x80000000
进行比较,将其转换为long long inttypes。
很明显,0小于0x80000000
。
数字常量0x80000000
是unsigned int
types的。 如果我们用-0x80000000
做2s的赞美math,我们得到这个:
~0x80000000 = 0x7FFFFFFF 0x7FFFFFFF + 1 = 0x80000000
所以-0x80000000 == 0x80000000
。 和比较(0 < 0x80000000)
(因为0x80000000
是无符号的)是真的。
在考虑-
是数字常量的一部分时会出现混淆的地方。
在下面的代码0x80000000
是数字常量。 它的types只在这个上决定。 之后应用-
不会更改types 。
#define INT32_MIN (-0x80000000) long long bal = 0; if (bal < INT32_MIN )
原始的未经修改的数字常量是正数。
如果是十进制,那么赋值的types是第一个types,它将保存它: int
, long
, long long
。
如果常量是八进制或hex,它将得到保存它的第一个types: int
, unsigned
, long
, unsigned long
, long long
, unsigned long long
。
0x80000000
,在OP的系统上得到unsigned
或unsigned long
的types。 无论哪种方式,这是一些无符号的types。
-0x80000000
也是一些非零值,并且是一些无符号types,它大于0.当代码比较long long
, 值不会在比较的两侧改变,所以0 < INT32_MIN
为真。
另一个定义避免了这种好奇的行为
#define INT32_MIN (-2147483647 - 1)
让我们走在幻想的土地一段时间int
和unsigned
是48位。
然后0x80000000
适合int
,所以是int
types。 -0x80000000
则是一个负数,打印结果是不同的。
[回到真实的单词]
由于0x80000000
在符号types之前适合某种无符号types,因为它在some_unsigned_MAX
只比some_signed_MAX
大,所以它是一些无符号types。
C有一个规则,整数文字可能有signed
或unsigned
取决于它是否符合有signed
或unsigned
(整数推广)。 在一个32
位的机器上,文字0x80000000
将是unsigned
。 在32位机器上, -0x80000000
2的补-0x80000000
是0x80000000
。 因此,比较bal < INT32_MIN
在有signed
和unsigned
之间,并且在根据C规则进行比较之前, unsigned int
将被转换为long long
。
C11:6.3.1.8/1:
否则,如果具有有符号整数types的操作数的types可以表示具有无符号整数types的操作数types的所有值,则具有无符号整数types的操作数将转换为操作数的types有符号整数types。
因此, bal < INT32_MIN
总是true
。