为什么是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 

如果文字数字不能放在默认的inttypes中,它将尝试下一个更大的types,如上表所示。 所以对于常规的十进制整数文字,它是这样的:

  • 尝试int
  • 如果不适合,请尝试一下
  • 如果不适合,请尝试long long

尽pipeHex文字的行为不同! 如果文字不能像int这样的有符号types,它将首先尝试unsigned int然后继续尝试更大的types。 查看上表中的区别。

所以在32位系统上,你的文字0x80000000unsigned inttypes的。

这意味着您可以将一元运算符应用于文字,而不用调用实现定义的行为,就像在溢出有符号整数时一样。 相反,您将得到值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

这个整型文字0x80000000types是unsigned int

根据C标准(6.4.4.1整型常量)

5一个整数常量的types是其中可以表示其值的对应列表的第一个。

而这个整数常量可以用unsigned int的types表示。

所以这个expression

-0x80000000具有相同的unsigned inttypes。 而且它在二进制补码表示中具有相同的值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

数字常量0x80000000unsigned inttypes的。 如果我们用-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,它将保存它: intlonglong long

如果常量是八进制或hex,它将得到保存它的第一个types: intunsignedlongunsigned longlong longunsigned long long

0x80000000 ,在OP的系统上得到unsignedunsigned long的types。 无论哪种方式,这是一些无符号的types。

-0x80000000也是一些非零值,并且是一些无符号types,它大于0.当代码比较long long不会在比较的两侧改变,所以0 < INT32_MIN为真。


另一个定义避免了这种好奇的行为

 #define INT32_MIN (-2147483647 - 1) 

让我们走在幻想的土地一段时间intunsigned是48位。

然后0x80000000适合int ,所以是inttypes。 -0x80000000则是一个负数,打印结果是不同的。

[回到真实的单词]

由于0x80000000在符号types之前适合某种无符号types,因为它在some_unsigned_MAX只比some_signed_MAX大,所以它是一些无符号types。

C有一个规则,整数文字可能有signedunsigned取决于它是否符合有signedunsigned (整数推广)。 在一个32位的机器上,文字0x80000000将是unsigned 。 在32位机器上, -0x80000000 2的补-0x800000000x80000000 。 因此,比较bal < INT32_MIN在有signedunsigned之间,并且在根据C规则进行比较之前, unsigned int将被转换为long long

C11:6.3.1.8/1:

否则,如果具有有符号整数types的操作数的types可以表示具有无符号整数types的操作数types的所有值,则具有无符号整数types的操作数将转换为操作数的types有符号整数types。

因此, bal < INT32_MIN总是true

Interesting Posts