学校のプロジェクトでは、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
です。それは何かを変えますか?
Cでは、-2147483648
は整数定数ではありません。 2147483648
は整数定数であり、-
はそれに適用される単項演算子であり、定数式を生成します。 2147483648
の値がint
に収まらない(大きすぎる場合、2147483647
は通常最大の整数です)。したがって、整数定数の型はlong
です。あなたが観察する問題。 int
の下限に言及する場合は、INT_MIN
からマクロ<limits.h>
(ポータブルアプローチ)を使用するか、2147483648
に言及しないように注意してください。
printf("PRINTF(d) \t: %d\n", -1 - 2147483647);
問題は、-2147483648
が整数リテラルではないことです。単項否定演算子-
と整数2147483648
で構成される式で、int
sが32ビットの場合はint
には大きすぎます。コンパイラは、否定演算子を適用する前に2147483648
を表す適切なサイズの符号付き整数を選択するため、結果の型はint
よりも大きくなります。
int
sが32ビットであることを知っていて、読みやすさを損なわずに警告を回避したい場合は、明示的なキャストを使用します。
printf("PRINTF(d) \t: %d\n", (int)(-2147483648));
これは、32ビットint
sを使用した2の補数マシンでの動作の定義です。
理論上の移植性を高めるには、数字の代わりにINT_MIN
を使用し、テストする2の補数でないマシンを見つけた場所をお知らせください。
明確にするために、その最後の段落は部分的に冗談でした。 int
のサイズはさまざまであるため、「最小のint
」を意味する場合、INT_MIN
を使用するのは間違いありません。たとえば、まだ多くの16ビット実装があります。書き出す-231 常にその値を正確に意味する場合にのみ有用です。この場合、おそらくint
の代わりにint32_t
のような固定サイズの型を使用します。
2147483648
と2174483648
の違いに気付かない人のために、10進数で数字を書き出す代わりの方法が必要な場合がありますが、注意が必要です。
上記のように、32ビットの2の補数のマシンでは、(int)(-2147483648)
はより広い符号付き型として扱われるため、-2147483648
はオーバーフローせず、したがって明確に定義されます。ただし、(int)(-0x80000000)
については同じではありません。 0x80000000
はunsigned int
として扱われます(符号なし表現に適合するため)。 -0x80000000
は明確に定義されていますが(int
が32ビットの場合、-
は効果がありません)、結果のunsigned int
0x80000000
からint
はオーバーフローを伴います。オーバーフローを回避するには、16進定数を符号付き型(int)(-(long long)(0x80000000))
にキャストする必要があります。
同様に、左シフト演算子を使用する場合は注意が必要です。 1<<31
は、32ビット(またはそれより小さい)int
sの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-「結果は実装定義または実装定義信号のいずれかが発生します」と言います。