为什么0< -0x80000000?

时间:2015-12-09 15:31:05

标签: c signed numeric-limits numeric-conversion

我有一个简单的程序:

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

有人可以指出这个问题吗?

6 个答案:

答案 0 :(得分:359)

这非常微妙。

程序中的每个整数文字都有一个类型。它的类型由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文字的行为有所不同!如果文字不适合像[{1}}这样的签名类型,它会先尝试int然后继续尝试更大的类型。请参阅上表中的差异。

因此,在32位系统上,您的文字unsigned int的类型为0x80000000

这意味着您可以在文字上应用一元unsigned int运算符而不调用实现定义的行为,就像溢出有符号整数时一样。相反,您将获得值-,这是一个正值。

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

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

答案 1 :(得分:26)

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

仍然上应用一元减号会为您提供一个非零值的无符号类型。 (事实上​​,对于非零值x,您最终得到的值为UINT_MAX - x + 1。)

答案 2 :(得分:23)

此整数文字0x80000000的类型为unsigned int

根据C标准(6.4.4.1整数常数)

  

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

这个整数常量可以用unsigned int的类型表示。

所以这个表达

-0x80000000具有相同的unsigned int类型。而且它具有相同的价值 {2}补码表示中的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

答案 3 :(得分:12)

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

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

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

答案 4 :(得分:11)

认为-是数字常量的一部分时会出现混淆。

在下面的代码0x80000000中是数字常量。它的类型仅在此确定。之后会应用-不会更改类型

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

原始未加修饰的数字常量是正数。

如果是十进制,则分配的类型是第一种类型:intlonglong long

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

在OP的系统上

0x80000000获取unsignedunsigned long的类型。无论哪种方式,它都是一些无符号类型。

-0x80000000也是一些非零值并且是一些无符号类型,它大于0.当代码将其与long long进行比较时,不是在比较的两边改变了,所以0 < INT32_MIN是真的。

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

#define INT32_MIN        (-2147483647 - 1)

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

然后0x80000000适合intint类型也适合-0x800000000x80000000则为负数,打印结果不同。

[回到实词]

由于some_signed_MAX在签名类型之前符合某些无符号类型,因为它仅在some_unsigned_MAX内大于background: url('dropdown_arrow.png') no-repeat;,因此它是一些无符号类型。

答案 5 :(得分:8)

C有一个规则,即整数文字可以是signedunsigned取决于它是否适合signedunsigned(整数提升)。在32位机器上,文字0x80000000将为unsigned。在32位计算机上,-0x80000000的2 0x80000000补码为bal < INT32_MIN。因此,比较signed介于unsignedunsigned int之间,根据C规则进行比较long long将转换为bal < INT32_MIN

C11:6.3.1.8/1:

  

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

因此,true始终为Group