While working on a C++ project with Visual C++ Express 2010 I’ve found an interesting issue that I’d like to understand. The problem is that the result of my program is different if I compile in Debug or Release modes. I made a small program to reproduce it:
#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
As you see, in both cases the second argument is evaluated before the first one (that one I expected, if arguments are processed in some kind of LIFO stack); but in release the increment of the variable i is “optimized away” and delayed until the next iteration of the loop. That was unexpected, and I’d really would like to understand what’s going on.
Of course, I can easily “fix” my code by changing the loop to
for (int i = 0; i < 5; ++i) function(firstarg(i+1), secondarg(i));
which will give always the same result regardless of compilation parameters. But, still, what I would really like to understand the reasons behind this optimization of increments.
PS. By the way, I could not reproduce this problem with gcc under linux (Debug with -O0 flag, Release with -O3).
You are misunderstanding the results. The increment is not “optimized away” or delayed until the next iteration of the loop. You just have no way to see what the value of
iis before the next iteration. Try this:And you’ll see that it’s not delayed at all.
Your code is UB because you have an access of a variable and a modification of that same variable without an intervening sequence point. While you could get really crazy results, in fact you get both of the “expected” results, depending on your optimizations. The order in which function parameters are evaluated is unspecified.