为什么0 <-0x80000000?


253

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

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

谁能指出这个问题?


3
多少钱CHAR_BIT * sizeof(int)
5gon12eder 2015年

1
您是否尝试过打印bal?
瑞安·菲茨帕特里克

10
恕我直言,更有趣的是,这是真的-0x80000000,但对于假-0x80000000L-2147483648-2147483648L(GCC 4.1.2),所以问题是:为什么是INT文字 -0x80000000从字面INT不同 -2147483648
Andreas Fester 2015年

2
@Bathsheba我只是在在线编译器tutorialspoint.com/codingground.htm
Jayesh Bhoi 2015年

2
如果您曾经注意到(的某些形式)<limits.h>定义INT_MIN(-2147483647 - 1),那么您现在知道为什么。
zwol

Answers:


363

这是相当微妙的。

程序中的每个整数文字都有一个类型。它具有哪种类型由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 intlong long。该值0x80000000将保留,并且0小于0x80000000,因此是结果。

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


3
“ ...用-2147483648L替换文字,您将明确获得一个带符号的长整数。” 嗯,在一个32位的long系统2147483648L,将不适合在long,所以它成为long long然后-被施加-或所以我想。
chux-恢复莫妮卡2015年

2
@ASH因为int可以有的最大数量是0x7FFFFFFF。自己尝试:#include <limits.h> printf("%X\n", INT_MAX);
Lundin

5
@ASH不要将源代码中整数文字的十六进制表示与带符号数字的基础二进制表示混淆。字面0x7FFFFFFF写的源代码时总是正数,但你int当然变量可以包含原始的二进制数最多值0xFFFFFFFF的。
伦丁2015年

2
@ASH ìnt n = 0x80000000强制从无符号文字转换为带符号类型。将会发生什么取决于您的编译器-这是实现定义的行为。在这种情况下,它选择将整个文字显示在中int,以覆盖符号位。在其他系统上,可能无法表示类型,并且您调用未定义的行为-程序可能会崩溃。如果这样做int n=2147483648;,您将获得完全相同的行为,而该行为与十六进制表示完全无关。
伦丁2015年

3
一元如何-应用于无符号整数的解释可以扩展一点。我一直假设(尽管幸运的是,从未依赖过该假设)无符号值将被“提升”为有符号值,或者结果可能是不确定的。(老实说,这应该是编译错误;这- 3u甚至意味着什么?)
Kyle Strand

27

0x80000000unsigned值为2147483648 的文字。

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


23

此整数文字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与转换为long long int类型的无符号值进行比较0x80000000

显然0小于0x80000000


12

数字常量0x80000000的类型为unsigned int。如果我们-0x80000000对它进行2s补码运算,我们将得到:

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

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


假定为32位int。尽管这是一个非常常见的选择,但在任何给定的实现中,它int可能更窄或更宽。但是,对于这种情况,这是正确的分析。
John Bollinger 2015年

这与OP的代码无关,-0x80000000是无符号算法。~0x800000000是不同的代码。
MM

简单地说,这似乎是对我最好的正确答案。@MM他正在解释如何采用二进制补码。该答案专门解决负号对数字的影响。
章鱼

@Octopus负号在数字(!)上应用2的补码。尽管这似乎很清楚,但并未描述代码中会发生什么-0x80000000!实际上2的补码与这个问题完全无关。
MM

12

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

在下面的代码中0x80000000是数字常数。它的类型仅取决于此。之后将-应用,并且不会更改类型

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

未经修饰的原始数字常量为正。

如果是小数,然后被分配类型是将其持有第一类:intlonglong long

如果常数是八进制或十六进制,它会保持它的第一种类型:intunsignedlongunsigned longlong longunsigned long long

0x80000000,在OP的系统上获取unsigned或的类型unsigned long。无论哪种方式,它都是一些无符号类型。

-0x80000000也有一些非零值并且是一些无符号类型,它大于0。当码进行比较,为一个long long,该未在比较的2个侧改变,因此0 < INT32_MIN是真实的。


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

#define INT32_MIN        (-2147483647 - 1)

让我们走在梦幻之地了一段时间,其中intunsigned48位。

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

[返回实词]

由于0x80000000在签订类型之前一些未签名式配合,因为它不仅仅是更大some_signed_MAX范围内还没有some_unsigned_MAX,这是一些未签名的类型。


8

C有一个规则,整数文字可以是signedunsigned取决于它是否适合signedunsigned(整数提升)。在一个32位机字面0x80000000unsigned。2的补码-0x800000000x80000000 一个32位机器上。因此,根据C规则,将bal < INT32_MINsigned和之间unsigned进行比较,然后进行比较。unsigned intlong long

C11:6.3.1.8/1:

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

因此,bal < INT32_MIN始终是true

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.