MSDN Flash 2009/8/11 No.330 の答え - (1)

今回の MSDN Flashでは 2つのコードを掲載しました。今回は 1つ目のコードの問題について解説したいと思います。
掲載したコードは以下の通り

 int i1 = -2;
unsigned int i2 = 1;
char *s = "Hello";
printf("%s", s + 1 + (i1 + i2));

期待される実行結果は 32bit環境では “Hello” と表示、64bit環境では不定…大抵はアクセス違反例外によるプログラムの異常終了です。
直接値を代入して 4行目を展開すると、

 s + 1 + (i1 + i2) = s + 1 + (-2 + 1) = s + 1 - 1 = s

となりそうなものですが、なぜそうならないのでしょうか?その答えは Usual Arithmetic Conversions にあります。

整数同士の演算において片方が int でもう片方が unsigned int の場合 int は unsigned int に変換され、結果は unsigned int で与えられます。
今回のコードでは int i1 = -2, unsigned int i2 = 1 なので i1 + i2 は

 (unsigned int)i1 + i2 = (unsigned int)-2 + 1 = 0xFFFFFFFE + 1 = 0xFFFFFFFF

よって最終的な評価結果は

 s + 1 + (i1 + i2) = s + 1 + (0xFFFFFFFE + 1) = s + 1 + 0xFFFFFFFF = s + 0x100000000

となります。

32bit環境では 0x100000000 の 232 の剰余が 0 となるため問題は発覚せず期待された動作となります。これを整数のラップアラウンドと呼びます。一方、64bit環境では s + 0x100000000 が printf関数に渡されるためアクセス違反例外が発生します。
そもそも整数やポインタの演算における意図しないラップアラウンドは避けるべきですが、今回のように隠れていた問題が 64bit環境へ移行時に発覚することがあります。

MSDN Flash ニュースレター最新号および購読はこちらから