※対象バージョン:Java1.4

ほとんどの計算ロジックは Java であれ、C/C++ であれ、他の言語も共通のものが利用可能です。しかし一部の計算、しかも通常では考えられないような計算をさせるとコンパイラによる計算結果の違いが出てきます。結論を言うと C/C++ ではインクリメントの計算順序について明確に定義されていないことが理由です。

今回、なぜこのような記事を書いたかというと会社の同僚から以下の質問を受けました。

// 以下の計算結果が Java と C で異なるがなぜか?
int a = 0;
b = ++a + ++a + ++a;
b = a++ + a++ + a++;
b = ++a + ++a + ++a

/******* 結果 *******
b = ++a + ++a + ++a
Java: b = 6
C/C++: b = 7

b = a++ + a++ + a++
Java: b = 3
C/C++: b = 0
*********************/

 
この問に回答すべくいろいろ考えてみて、ここに書く内容に至りました。まずはみなさんも自分で思う計算方法でまずは計算してみてください。Java と C/C++ のどちらと等しい結果となったでしょうか?

なお、言語使用は一切見ていないため、あくまでも想像の話なのでご注意ください。

Java と一致した考えの方、Java では以下のような計算がされていると考えられます。

// Java の計算
fx(0) = 0 // 初期値を 0 に設定
fx(n) = fx(n - 1) + 1 // n 番目の値は前の結果に 1 を加算
fy = fx(1) + fx(2) + fx(3) // ++a + ++a + ++a

fx(1) = fx(0) + 1 = 1 // ①...1 番目がインクリメント
fx(2) = fx(1) + 1 = 2 // ②...2 番目がインクリメント
fx(3) = fx(2) + 1 = 3 // ③...3 番目がインクリメント
fy = fx(1) + fx(2) + fx(3) = 1 + 2 + 3 = 6

 
数学を学んでいる人であれば、このように考えるでしょう。しかしプログラムの世界では a が変数である以上、++a ⇒ a であると考えることができます。つまり fx(n) = fx(n – 1) + 1 は fy = fx + fx + fx となります。結果が C/C++ と一致した人はこのようなプログラムの世界の考え方をしたのではないでしょうか?

// C/C++ の計算
fy = fx + fx + fx // 初期値 fx = 0
fy = ++fx + fx + fx // fx = 1 (fx = fx + 1)
fy = fx + ++fx + fx // fx = 2 (fx = fx + 1)
fy = (fx + fx) + fx = (2 + 2) + fx = 4 + fx // fx = 2、1 番目と 2 番目の加算
fy = 4 + ++fx // fx = 3 (fx = fx + 1)
fy = 4 + 3 = 7 // fx = 3、1 番目と 2 番目の加算

 
このように同一変数のインクリメントを含む計算を行う場合、コンパイラの違いを意識する必要があります。非常にリスクの高い計算であることはご理解頂けたと思います、推奨はされません。今回の件に限らず変にテクニックを見せると後で痛い目を見ることになるため注意しましょう。



Leave a Reply

preload preload preload