なぜ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を印刷してみましたか?
Ryan Fitzpatrick

10
私見より興味深いのは、それが真実であることであるだけのために-0x80000000、しかしための偽の-0x80000000L-2147483648そして-2147483648Lなぜintがリテラルである:質問があるので、(GCC 4.1.2) -0x80000000int型のリテラルは異なりますか -2147483648
Andreas Fester

2
@Bathshebaオンラインコンパイラtutorialspoint.com/codingground.htmで
Jayesh Bhoi

2
(の一部の化身)がとして<limits.h>定義さINT_MINれていることに気付いたことがあれば、その(-2147483647 - 1)理由を理解してください。
zwol

回答:


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型に収まらない場合は、上記の表に示されているように、次に大きい型を試みます。したがって、通常の10進整数リテラルの場合は次のようになります。

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

16進リテラルの動作は異なりますが!リテラルがのような署名された型の内部に収まらない場合、より大きな型のint試行unsigned intに進む前に、最初に試行します。上記の表の違いをご覧ください。

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

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

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

リテラルを2147483648L10進表記で置き換えると、コンパイラーはを選択せずunsigned int、内に収めようとしlongます。また、L接尾辞は、long 可能であれば必要であることを示しています。あなたは6.4.4.1で述べたテーブルを読み続ける場合はLサフィックスは、実際に同様のルールがあります。数は、要求された内側にフィットしていない場合long、それは32ビットの場合には、コンパイラはあなたを与えるだろうしない、long longそれをどこにうまくフィットします。


3
「...リテラルを-2147483648Lに置き換えると、明示的にlongが取得され、署名されます。」うーん、32ビットではlongシステム2147483648Lに適合しないlongことになるので、long longその後、-またはそう私は思った-適用されます。
chux-モニカを

2
@ASH intが持つことができる最大数はそのとき0x7FFFFFFFです。それを自分で試してみてください#include <limits.h> printf("%X\n", INT_MAX);
ランディン

5
@ASHソースコード内の整数リテラルの16進数表現と、基になる符号付き数値のバイナリ表現を混同しないでください。0x7FFFFFFFソースコードで記述されたリテラルは常に正の数ですが、int変数にはもちろん値0xFFFFFFFFまでの生の2進数を含めることができます。
ランディン

2
@ASH ìnt n = 0x80000000は、符号なしリテラルから符号付き型への変換を強制します。何が起こるかはコンパイラ次第です-それは実装定義の振る舞いです。この場合、リテラル全体をに表示してint、符号ビットを上書きすることを選択しました。他のシステムでは、型を表すことができない場合があり、未定義の動作を呼び出す-プログラムがクラッシュする可能性があります。int n=2147483648;これを行うと、16進表記とはまったく関係なく、まったく同じ動作が得られます。
ランディン

3
単項-が符号なし整数にどのように適用されるかについての説明は少し拡張できます。私はいつも(幸いなことにこの仮定に依存したことはありませんが)符号なしの値は符号付きの値に「昇格」されるか、結果は未定義であると常に想定していました。(正直なところ、それはコンパイルエラーであるはずです。どういう- 3u意味ですか?)
カイルストランド

27

0x80000000あるunsigned値2147483648を持つリテラル。

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


23

この整数リテラルに0x80000000はタイプがありますunsigned intです。

C標準に準拠(6.4.4.1整数定数)

5整数定数の型は、その値を表すことができる対応するリストの最初のものです。

そして、この整数定数は、次のタイプで表すことができます。 unsigned int

だからこの表現

-0x80000000同じunsigned intタイプです。さらに0x80000000、次のように計算する2の補数表現で同じ値を持って います

-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


12

数値定数0x80000000のタイプはunsigned intです。-0x800000002の補数計算を取り、それを実行すると、次のようになります。

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

ですから-0x80000000 == 0x80000000。そして(0 < 0x80000000)0x80000000(署名されていないため)比較は真です。


これは、32ビットintsを想定しています。これは非常に一般的な選択ですが、特定の実装でintは狭くても広くてもかまいません。ただし、その場合の正しい分析です。
John Bollinger、2015

これはOPのコードに-0x80000000は関係なく、符号なし演算です。~0x800000000別のコードです。
MM

これは簡単に言うと私にとって最良かつ正しい答えのようです。@MM彼は2の補数を取る方法を説明しています。この答えは、負の符号が数値に対して何をしているのかを具体的に示しています。
Octopus

@Octopusの負符号は、数値に2の補数を適用していません(!)これは明らかなようですが、コードで何が発生するかを説明していません-0x80000000。実際、2の補数はこの質問にはまったく関係ありません。
MM

12

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

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

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

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

それが小数の場合は、割り当てられたタイプがそれを保持する第1のタイプは次のとおりです。intlonglong long

定数は8進数または16進数の場合は、それを保持する第1のタイプを取得します:intunsignedlongunsigned longlong longunsigned long long

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

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


別の定義では、この奇妙な動作を回避します

#define INT32_MIN        (-2147483647 - 1)

私たちはしばらくの間、ファンタジーの土地で見てみましょうどこintunsigned 48ビットです。

その後、0x80000000収まりint、タイプも変わりますint-0x80000000は負の数であり、印刷結果は異なります。

[本当の言葉に戻る]

以来0x80000000signed型の前にいくつかのunsigned型に収まることがよりちょうど大きいほどsome_signed_MAX内にはまだsome_unsigned_MAX、それはいくつかの符号なしのタイプです。


8

Cは、整数リテラルがあり得ることをルール有するsigned又はunsignedそれに適合するかどうかに依存してsigned、またはunsigned(整数プロモーション)。上32ビットマシンリテラルは0x80000000なりますunsigned。2の補数-0x800000000x80000000 32ビットマシン上で。したがって、Cルールによると、比較bal < INT32_MINはとの間signedで行われ、次のように変換されます。unsignedunsigned intlong longます。

C11:6.3.1.8/1:

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.