web-dev-qa-db-ja.com

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 )は常に真です。どうして可能ですか?

マクロを次のように変更するとうまくいきます:

#define INT32_MIN        (-2147483648L)

誰でも問題を指摘できますか?

250
Jayesh Bhoi

これは非常に微妙です。

プログラムのすべての整数リテラルには型があります。どのタイプがあるかは、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型に収まらない場合、上の表に示されているように、次に大きい型を試行します。したがって、通常の10進整数リテラルの場合、次のようになります。

  • intを試してください
  • 収まらない場合は、longを試してください
  • 収まらない場合は、long longを試してください。

ただし、16進リテラルは異なる動作をします!リテラルがintのような符号付きの型に収まらない場合、最初にunsigned intを試行してから、より大きな型を試行します。上記の表の違いをご覧ください。

したがって、32ビットシステムでは、リテラル0x80000000unsigned int型です。

これは、符号付き整数をオーバーフローさせる場合のように、実装定義の動作を呼び出さずに、リテラルに単項-演算子を適用できることを意味します。代わりに、値0x80000000、正の値を取得します。

bal < INT32_MINは通常の算術変換を呼び出し、式0x80000000の結果はunsigned intからlong longにプロモートされます。値0x80000000は保持され、0は0x80000000より小さいため、結果が得られます。

リテラルを2147483648Lで置き換える場合、10進表記を使用するため、コンパイラーはunsigned intを選択せず​​、long内に収めようとします。また、Lサフィックスは、long可能であればが必要であることを示しています。 6.4.4.1で言及された表を読み続けると、L接尾辞には実際に同様の規則があります:数値が要求されたlong内に収まらない場合(32ビットの場合は収まらない場合)、コンパイラーはlong longを提供しますそれがうまく収まる場所。

359
Lundin

0x80000000は、値2147483648のunsignedリテラルです。

このstillに単項マイナスを適用すると、ゼロ以外の値を持つ符号なしの型が得られます。 (実際、ゼロ以外の値xの場合、最終的な値はUINT_MAX - x + 1です。)

26
Bathsheba

この整数リテラル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

0nsigned value 0x80000000と比較され、通常の算術変換の規則に従ってlong long int型に変換されます。

0が0x80000000より小さいことは明らかです。

23

数値定数0x80000000は、タイプunsigned intです。 -0x80000000を使用して2の補数計算を行うと、次のようになります。

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

-0x80000000 == 0x80000000(0 < 0x80000000)の比較(0x80000000は符号なし)が真です。

12
dbush

-が数値定数の一部であると考えると混乱が生じます。

以下のコードの0x80000000は数値定数です。そのタイプはその上でのみ決定されます。 -が後で適用され、タイプを変更しません

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

Raw装飾されていない数値定数は正です。

10進数の場合、割り当てられる型は、それを保持する最初の型(intlonglong long)です。

定数が8進数または16進数の場合、定数を保持する最初の型(intunsignedlongunsigned longlong longunsigned long long)を取得します。

0x80000000、OPのシステムでは、unsignedまたはunsigned longのタイプを取得します。いずれにせよ、それは何らかの符号なしの型です。

-0x80000000もゼロ以外の値であり、符号なしの型であるため、0より大きくなります。コードをlong longと比較する場合、比較の2辺でvaluesは変更されないため、0 < INT32_MIN本当です。


別の定義により、この奇妙な動作を回避できます

#define INT32_MIN        (-2147483647 - 1)

intunsignedが48ビットであるファンタジーの世界をしばらく歩きましょう。

その後、0x80000000intに適合し、タイプintも適合します。 -0x80000000は負の数であり、印刷結果は異なります。

[実話に戻る]

0x80000000は、some_signed_MAXよりも大きいだけでまだsome_unsigned_MAX内にあるため、符号付き型の前にある符号なしの型に収まるため、符号なしの型です。

11
chux

Cには、整数リテラルがsignedまたはunsignedになる可能性があるというルールがあり、それがsignedまたはunsigned(整数プロモーション)に適合するかどうかによって決まります。 32- bitマシンでは、リテラル0x80000000unsignedになります。 -0x80000000の2の補数は、32ビットマシンでは0x80000000です。したがって、比較bal < INT32_MINsignedunsignedの間にあり、Cルールunsigned intによる比較の前にlong longに変換されます。

C11:6.3.1.8/1:

[...]それ以外の場合、符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドはを持つオペランドの型に変換されます符号付き整数型。

したがって、bal < INT32_MINは常にtrueです。

8
haccks