Visual C++ Express 2010 を使用して C++ プロジェクトに取り組んでいるときに、理解したい興味深い問題を見つけました。問題は、デバッグ モードまたはリリース モードでコンパイルすると、プログラムの結果が異なることです。それを再現するための小さなプログラムを作成しました。
#include <stdio.h>
int firstarg(int i)
{
printf("First argument called with i = %d\n", i);
return i;
}
int secondarg(int i)
{
printf("Second argument called with i = %d\n", i);
return i;
}
void function(int i, int j)
{
printf("Function called with %d, %d\n", i,j);
}
int main(int argc, char* argv[])
{
// Line with the problem!
for (int i = 0; i < 5; ) function(firstarg(i), secondarg(i++));
return 0;
}
// Result on RELEASE:
Second argument called with i = 0
First argument called with i = 0
Function called with 0, 0
Second argument called with i = 1
First argument called with i = 1
Function called with 1, 1
Second argument called with i = 2
First argument called with i = 2
Function called with 2, 2
Second argument called with i = 3
First argument called with i = 3
Function called with 3, 3
Second argument called with i = 4
First argument called with i = 4
Function called with 4, 4
// Result on DEBUG
Second argument called with i = 0
First argument called with i = 1
Function called with 1, 0
Second argument called with i = 1
First argument called with i = 2
Function called with 2, 1
Second argument called with i = 2
First argument called with i = 3
Function called with 3, 2
Second argument called with i = 3
First argument called with i = 4
Function called with 4, 3
Second argument called with i = 4
First argument called with i = 5
Function called with 5, 4
ご覧のとおり、どちらの場合も、2 番目の引数は最初の引数の前に評価されます (引数が何らかの LIFO スタックで処理される場合、私が期待したもの)。ただし、リリースでは、変数 i のインクリメントは「最適化されて」、ループの次の反復まで遅延されます。それは予想外だったので、何が起こっているのか本当に知りたいです。
もちろん、ループを次のように変更することで、コードを簡単に「修正」できます。
for (int i = 0; i < 5; ++i) function(firstarg(i+1), secondarg(i));
コンパイルパラメータに関係なく、常に同じ結果が得られます。それでも、このインクリメントの最適化の背後にある理由を本当に理解したいと思っています。
PS。ちなみに、Linux の gcc ではこの問題を再現できませんでした (-O0 フラグでデバッグ、-O3 フラグでリリース)。