Several answers have indicated that while different platforms might give different results, the result is deterministic on a given platform.
This is not correct
The C99 Standard says (6.5/3 Expressions):
Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
So, the order of evaluation of the parameters in the call to foo()
is not specified by the standard. The order that the 2 calls to counter()
cannot be counted on. A particular compiler could order the calls differently depending on:
- the optimizations the compiler is asked to perform
- the exact set of code (include files, slightly or significantly different source code in the translation unit, whatever)
- the day of the week the program is built
- a random number
While it's unlikely that things other than the optimizations used, differences in other compiler options, or differences in the translation unit will result in a different ordering of the argument evaluation (since there probably wouldn't be much reason for the compiler to generate different output), the fact is you simply can't depend on the ordering.
In fact, it's even OK (as far as the standard is concerned) for the order of evaluation of the call to be made differently each time foo()
is invoked. For example, say your example program looked like (to make what's happening when more obvious):
#include <stdio.h>
int i = 1;
int counter1(){
i = i * 3;
printf( "counter1()\n");
return i;
}
int counter2(){
i = i * 5;
printf( "counter2()\n");
return i;
}
int foo(int i, int j){
return i + j;
}
int main(){
int x;
for (x=0; x<2; ++x) {
printf("%d\n", foo(counter1(), counter2()));
}
return 0;
}
It would be perfectly valid for the output to look like any of the following (note there's at least one additional possibility):
Possibility 1:
counter1()
counter2()
18
counter1()
counter2()
270
Possibility 2:
counter1()
counter2()
18
counter2()
counter1()
300
Possibility 3:
counter2()
counter1()
20
counter2()
counter1()
300
It would be OK (even if very weird) for the compiler to evaluate the arguments differently each time that line of code is executed, but it's permitted by the fact that the order is unspecified by the standard.
While it's highly unlikely that the evaluation would be 'randomized', I do think that such difficult to control things as the optimization level (or other compiler settings), the precise version/patch level of the compiler, or even the exact code that surrounds the expressions could cause the compiler to chose to a different evaluation path.
Relying on the order of evaluation of function arguments, even on a particular platform, is flirting with danger.
As a side note, this is one of the reasons that having hidden side-effects in a function is something to avoid if possible.