web-dev-qa-db-ja.com

最小のint、-2147483648のタイプが「long」なのはなぜですか?

学校のプロジェクトでは、C関数printfをコーディングする必要があります。物事は順調に進んでいますが、良い答えを見つけることができない質問が1つあります。

printf("PRINTF(d) \t: %d\n", -2147483648);

教えて(gcc -Werror -Wextra -Wall):

   error: format specifies type 'int' but the argument has type 'long'
      [-Werror,-Wformat]
        printf("PRINTF(d) \t: %d\n", -2147483648);
                              ~~     ^~~~~~~~~~~
                              %ld

しかし、int変数を使用すると、すべてうまくいきます。

int i;

i = -2147483648;
printf("%d", i);

どうして?

編集:

私は多くの点を理解し、それらは非常に興味深いものでした。とにかく、printf<stdarg.h>ライブラリを使用しているので、va_arg(va_list ap, type)も正しい型を返す必要があります。 %dおよび%iの場合、明らかに返される型はintです。それは何かを変えますか?

163

Cでは、-2147483648は整数定数ではありません。 2147483648は整数定数であり、-はそれに適用される単項演算子であり、定数式を生成します。 2147483648の値がintに収まらない(大きすぎる場合、2147483647は通常最大の整数です)。したがって、整数定数の型はlongです。あなたが観察する問題。 intの下限に言及する場合は、INT_MINからマクロ<limits.h>(ポータブルアプローチ)を使用するか、2147483648に言及しないように注意してください。

printf("PRINTF(d) \t: %d\n", -1 - 2147483647);
225
fuz

問題は、-2147483648が整数リテラルではないことです。単項否定演算子-と整数2147483648で構成される式で、intsが32ビットの場合はintには大きすぎます。コンパイラは、否定演算子を適用する前に2147483648を表す適切なサイズの符号付き整数を選択するため、結果の型はintよりも大きくなります。

intsが32ビットであることを知っていて、読みやすさを損なわずに警告を回避したい場合は、明示的なキャストを使用します。

printf("PRINTF(d) \t: %d\n", (int)(-2147483648));

これは、32ビットintsを使用した2の補数マシンでの動作の定義です。

理論上の移植性を高めるには、数字の代わりにINT_MINを使用し、テストする2の補数でないマシンを見つけた場所をお知らせください。


明確にするために、その最後の段落は部分的に冗談でした。 intのサイズはさまざまであるため、「最小のint」を意味する場合、INT_MINを使用するのは間違いありません。たとえば、まだ多くの16ビット実装があります。書き出す-231 常にその値を正確に意味する場合にのみ有用です。この場合、おそらくintの代わりにint32_tのような固定サイズの型を使用します。

21474836482174483648の違いに気付かない人のために、10進数で数字を書き出す代わりの方法が必要な場合がありますが、注意が必要です。

上記のように、32ビットの2の補数のマシンでは、(int)(-2147483648)はより広い符号付き型として扱われるため、-2147483648はオーバーフローせず、したがって明確に定義されます。ただし、(int)(-0x80000000)については同じではありません。 0x80000000unsigned intとして扱われます(符号なし表現に適合するため)。 -0x80000000は明確に定義されていますが(intが32ビットの場合、-は効果がありません)、結果のunsigned int0x80000000からintはオーバーフローを伴います。オーバーフローを回避するには、16進定数を符号付き型(int)(-(long long)(0x80000000))にキャストする必要があります。

同様に、左シフト演算子を使用する場合は注意が必要です。 1<<31は、32ビット(またはそれより小さい)intsの32ビットマシンでは未定義の動作です。 2にのみ評価されます31 intが少なくとも33ビットの場合、kビットによる左シフトは、kが整数型の非符号ビットの数より厳密に小さい場合にのみ明確に定義されるため左側の引数。

1LL<<31は安全です。long long intは2を表すことができる必要があるためです63-1。したがって、ビットサイズは32より大きくなければなりません。

(int)(-(1LL<<31))

おそらく最も読みやすいです。 YMMV。


すべての合格者について、この質問にはCというタグが付けられ、最新のCドラフト(n1570.pdf)は、E1 << E2に関してE1が符号付きの型である場合、値はE1は負ではなく、E1 × 2E2は「結果の型で表現可能」です。 (§6.5.7パラ4)。

E1が非負でE1 × 2E2が表現可能な場合、左シフト演算子の適用が定義されるC++とは異なります対応する符号なし型で "(§5.8para。2、強調を追加)。

C++では、最新のドラフト標準に従って、整数値の符号付き整数型への変換は実装定義値が宛先型で表現できない場合(§4.7段落3) 。 C標準の対応する段落-§6.3.1.3パラ3-「結果は実装定義または実装定義信号のいずれかが発生します」と言います。

59
rici