このプログラムはarray
の要素を出力することになっていますが、実行すると出力は表示されません。
#include <stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = { 23, 34, 12, 17, 204, 99, 16 };
int main() {
int d;
for (d = -1; d <= (TOTAL_ELEMENTS - 2); d++)
printf("%d\n", array[d + 1]);
return 0;
}
このプログラムに出力が表示されないのはなぜですか?
sizeof
は符号なし整数を返すため、_TOTAL_ELEMENTS
_も符号なしです。
d
が署名されています。最初は、d
は_-1
_です。ただし、比較を実行すると、d
は暗黙的に符号なしに型キャストされるため、_-1
_と比較したときに_TOTAL_ELEMENTS
_ではなくなり、実際は_UINT_MAX
_(私のマシンでは_4294967295
_ですが、他のマシンでは異なる場合があります)。
また、
これを修正したい場合は、_TOTAL_ELEMENTS
_をint
に型キャストしてください:
_for(d = -1; d <= (int)(TOTAL_ELEMENTS - 2); d++)
_
これは印刷されます:
_23
34
12
17
204
99
16
_
ご想像の通り。また、符号付き符号なし比較のトピックの詳細については、 符号なし整数と符号付き整数の比較操作 を参照してください。
コンパイラの警告を有効にすると、何が起こっているのかを理解するのに役立つことに注意してください(hydeの comment で確認されています)。
_$ gcc -Wall -Wextra test.c
test.c:7:17: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
for(d = 0; d < TOTAL_ELEMENTS; d++)
~ ^ ~~~~~~~~~~~~~~
1 warning generated.
_
あるいは、d
を_0
_で開始し、代わりに_TOTAL_ELEMENTS - 1
_を実行してみませんか?タイプキャストをドロップすることもできます。これは_d = -1
_のコーナーケースにのみ必要です。
_for(d = 0; d < TOTAL_ELEMENTS; d++)
printf("%d\n", array[d]);
_
脚注として、関連するC99標準の抜粋を以下に示します。
6.3.1.8p2は、符号付きから符号なしのタイプへの変換を定義します。
符号なし整数型のオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。
6.3.1.3p2は、変換の実行方法を定義します:_UINT_MAX + 1
_を署名付き表現に追加します。
新しい型が符号なしの場合、値は、値が新しい型の範囲内になるまで、新しい型で表現できる最大値よりも1多い値を繰り返し加算または減算することにより変換されます。
したがって、このシナリオでは、_-1
_ => -1 + (UINT_MAX + 1)
= _UINT_MAX
_です。
私のgccはこの警告を出力します:
warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
for(d = 0; d < TOTAL_ELEMENTS; d++)
つまり、(TOTAL_ELEMENTS-2)
はunsigned int
で、d
はsigned int
です。これにより、(unsigned int)(-1) > (TOTAL_ELEMENTS-2)
以来、式はfalse
の初期値に対して常にd
になります。
異なる整数型間の二項演算は、いわゆる通常の算術変換によって定義される「共通」型内で実行されます。したがって、int dは、値-1で初期化されたシンギングタイプです。 unsigned intに変換すると、TOTAL_ELEMENTSによって返される値よりもはるかに大きい最大のunsigned intを返します。