私は以下の簡単なプログラムを持っています:
#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.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より小さいため、結果が得られます。
リテラルを2147483648L
で置き換える場合、10進表記を使用するため、コンパイラーはunsigned int
を選択せず、long
内に収めようとします。また、Lサフィックスは、long
可能であればが必要であることを示しています。 6.4.4.1で言及された表を読み続けると、L接尾辞には実際に同様の規則があります:数値が要求されたlong
内に収まらない場合(32ビットの場合は収まらない場合)、コンパイラーはlong long
を提供しますそれがうまく収まる場所。
0x80000000
は、値2147483648のunsigned
リテラルです。
このstillに単項マイナスを適用すると、ゼロ以外の値を持つ符号なしの型が得られます。 (実際、ゼロ以外の値x
の場合、最終的な値はUINT_MAX - x + 1
です。)
この整数リテラル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
がnsigned value 0x80000000
と比較され、通常の算術変換の規則に従ってlong long int型に変換されます。
0が0x80000000
より小さいことは明らかです。
数値定数0x80000000
は、タイプunsigned int
です。 -0x80000000
を使用して2の補数計算を行うと、次のようになります。
~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000
-0x80000000 == 0x80000000
。 (0 < 0x80000000)
の比較(0x80000000
は符号なし)が真です。
-
が数値定数の一部であると考えると混乱が生じます。
以下のコードの0x80000000
は数値定数です。そのタイプはその上でのみ決定されます。 -
が後で適用され、タイプを変更しません。
#define INT32_MIN (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )
Raw装飾されていない数値定数は正です。
10進数の場合、割り当てられる型は、それを保持する最初の型(int
、long
、long long
)です。
定数が8進数または16進数の場合、定数を保持する最初の型(int
、unsigned
、long
、unsigned long
、long long
、unsigned long long
)を取得します。
0x80000000
、OPのシステムでは、unsigned
またはunsigned long
のタイプを取得します。いずれにせよ、それは何らかの符号なしの型です。
-0x80000000
もゼロ以外の値であり、符号なしの型であるため、0より大きくなります。コードをlong long
と比較する場合、比較の2辺でvaluesは変更されないため、0 < INT32_MIN
本当です。
別の定義により、この奇妙な動作を回避できます
#define INT32_MIN (-2147483647 - 1)
int
とunsigned
が48ビットであるファンタジーの世界をしばらく歩きましょう。
その後、0x80000000
はint
に適合し、タイプint
も適合します。 -0x80000000
は負の数であり、印刷結果は異なります。
[実話に戻る]
0x80000000
は、some_signed_MAX
よりも大きいだけでまだsome_unsigned_MAX
内にあるため、符号付き型の前にある符号なしの型に収まるため、符号なしの型です。
Cには、整数リテラルがsigned
またはunsigned
になる可能性があるというルールがあり、それがsigned
またはunsigned
(整数プロモーション)に適合するかどうかによって決まります。 32
- bitマシンでは、リテラル0x80000000
はunsigned
になります。 -0x80000000
の2の補数は、32ビットマシンでは0x80000000
です。したがって、比較bal < INT32_MIN
はsigned
とunsigned
の間にあり、Cルールunsigned int
による比較の前にlong long
に変換されます。
[...]それ以外の場合、符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドはを持つオペランドの型に変換されます符号付き整数型。
したがって、bal < INT32_MIN
は常にtrue
です。