为什么0 <0 x80000000吗?

我下面有一个简单的程序:

#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)

有人能指出这个问题吗?

17801 次浏览

0x80000000是一个unsigned字面值,值为2147483648。

在这个仍然上应用一元减号会得到一个非零值的unsigned类型。(事实上,对于非零值x,你最终得到的值是UINT_MAX - x + 1。)

数值常量0x80000000的类型是unsigned int。如果我们使用-0x80000000并对其进行2s恭维数学运算,我们会得到:

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

所以-0x80000000 == 0x80000000。并且比较(0 < 0x80000000)(因为0x80000000是无符号的)是真的。

这个整数字面值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转换为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
  • 如果它不适合,尝试long
  • 如果它不适合,尝试long long

但十六进制文字的表现不同!如果字面量不能放入像int这样的符号类型中,它将首先尝试unsigned int,然后再尝试更大的类型。请参见上表中的差异。

所以在32位系统中,你的字面量0x80000000是类型unsigned int的。

这意味着你可以在字面上应用一元-运算符,而不调用实现定义的行为,就像你在溢出有符号整数时那样。相反,你会得到值0x80000000,一个正值。

bal < INT32_MIN调用通常的算术转换,表达式0x80000000的结果从unsigned int提升到long long。值0x80000000被保留,0小于0x80000000,因此结果为。

当你用2147483648L替换字面量时,你使用的是十进制计数法,因此编译器不会选择unsigned int,而是试图将它放入long中。另外,L后缀表示你想要一个long 如果可能的话。如果你继续阅读6.4.4.1中提到的表格,L后缀实际上有类似的规则:如果数字不适合在请求的long中,在32位的情况下是不适合的,编译器会给你一个long long,它将很好地适合。

C有一个规则,整型字面值可以是signedunsigned,这取决于它是否适合signedunsigned(整型提升)。在__abc4位机器上,字面量0x80000000将是unsigned。2对-0x80000000的补充在32位机器上是0x80000000。因此,比较bal < INT32_MIN是在signedunsigned之间,根据C规则,在比较之前unsigned2将被转换为unsigned3。

C11: 6.3.1.8/1:

[…否则,如果带符号整型的操作数的类型可以表示带符号整型的操作数类型的所有值,则带符号整型的操作数将转换为带符号整型的操作数的类型。

因此,bal < INT32_MIN始终是true

在认为-是数值常量的一部分时,出现了一个混淆点。

在下面的代码中,0x80000000是数值常量。它的类型仅在此基础上确定。随后应用-不改变类型. conf。

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

未修饰的数字常量为正数。

如果是十进制,则分配的类型是第一个保存它的类型:intlonglong long

如果常量是八进制或十六进制,它将获得包含它的第一个类型:intunsignedlongunsigned longlong longunsigned long long

0x80000000,在OP的系统上获取类型为unsignedunsigned long。无论哪种方式,它都是某种无符号类型。

-0x80000000也是一个非零值,并且是一个无符号类型,它大于0。当代码将其与long long进行比较时,比较的两边的没有改变,因此0 < INT32_MIN为真。


另一种定义可以避免这种奇怪的行为

#define INT32_MIN        (-2147483647 - 1)

让我们在intunsigned是48位的幻想世界中漫步一会儿。

然后,0x80000000适合int,类型int也是如此。-0x80000000是一个负数,打印出来的结果是不同的。

[回到现实]

由于0x80000000适合于有符号类型之前的某个无符号类型,因为它只是在some_unsigned_MAX中比some_signed_MAX大,因此它是某个无符号类型。