会社で他の人が作ったコードをデバッグしているときに見つけた、ひどい罠。
例えば、下記コードにおいて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モードでの結果から。
inputはともに0, sum=0, p=2となる。どうやら前の例と同様の挙動をしているようだ。
次に、debugモードでの実行結果を示す。
先ほどとは結果が変わり、まずinput=1が現れ、その後input=0が現れ、sumとpは予想通りsum=1, p=2となる。
どうやら、debugモードでは関数の引数ならば個別に評価されるようだ。しかし、input=1が最初に現れるのが気になる。呼び出し順はどうなっているのだろうか?これを調べるために、下記コードを作成してみた。
An example of code including 2 increments in a lin ...
debugモードでの結果は以下のようになった。
どうやら、まずは関数呼び出し無しで演算可能な部分を左から順に実行し、ついで関数呼び出し必要な演算を右から順にしている模様?
releaseモードでは以下のようになった。
演算順はdebugモードと同じで、値の更新が必要な部分は後からまとめて更新する挙動のようだ。
この挙動に気づくまで、1時間ぐらいかかった・・・。
教訓:一つの式に、インクリメント演算子を複数入れてはならない。または、もっと広く「一つの式に、同一の変数を操作する関数・演算子を複数入れてはならない。」
教訓:MSDNに「演算の順序は、言語によって定義されていません。 コンパイラは、一貫性のある結果を保証できる場合には、このような式を任意の順序で自由に評価します。」などと書いてあっても信用しない。
誰だよこんなコード書いたの・・・ほんとにもう・・・。