ドナドナされるプログラマのメモ

Windows用アプリのプログラミングメモ

式の中で複数のインクリメントは危険

会社で他の人が作ったコードをデバッグしているときに見つけた、ひどい罠。

例えば、下記コードにおいてsum, pはどう表示されるだろうか?

An example of code including 2 increments in a lin ...

 

これ、sum=1, p=2となりそうだが、少なくともVisual C++ 2017 00369-60000-00001-AA379ではsum=0, p=2となる。どうやら、何らかの最適化により最初にsumを計算し、その後インクリメントをしているようだ。この最適化は無効にできないようで、色々試したが結果は変わらなかった。

では、次のコードではsum, pはどう表示されるだろうか?

Example of code including 2 increments in a line

これの実行結果はinput=0 input=1 sum=1 p=2となりそうである。しかしVisual C++ 2017 00369-60000-00001-AA379では異なる結果となる。しかも、debugモードとreleaseモードでは実行結果が変わる。

まずはreleaseモードでの結果から。

f:id:donadonasan:20180512164507p:plain

inputはともに0, sum=0, p=2となる。どうやら前の例と同様の挙動をしているようだ。

次に、debugモードでの実行結果を示す。

f:id:donadonasan:20180512164942p:plain

先ほどとは結果が変わり、まずinput=1が現れ、その後input=0が現れ、sumとpは予想通りsum=1, p=2となる。

どうやら、debugモードでは関数の引数ならば個別に評価されるようだ。しかし、input=1が最初に現れるのが気になる。呼び出し順はどうなっているのだろうか?これを調べるために、下記コードを作成してみた。

An example of code including 2 increments in a lin ...

debugモードでの結果は以下のようになった。

f:id:donadonasan:20180512170314p:plain

どうやら、まずは関数呼び出し無しで演算可能な部分を左から順に実行し、ついで関数呼び出し必要な演算を右から順にしている模様?

releaseモードでは以下のようになった。

f:id:donadonasan:20180512170616p:plain

演算順はdebugモードと同じで、値の更新が必要な部分は後からまとめて更新する挙動のようだ。

この挙動に気づくまで、1時間ぐらいかかった・・・。

教訓:一つの式に、インクリメント演算子を複数入れてはならない。または、もっと広く「一つの式に、同一の変数を操作する関数・演算子を複数入れてはならない。」

教訓:MSDN「演算の順序は、言語によって定義されていません。 コンパイラは、一貫性のある結果を保証できる場合には、このような式を任意の順序で自由に評価します。」などと書いてあっても信用しない。

誰だよこんなコード書いたの・・・ほんとにもう・・・。