This is a catalogue of some experiments on just two aspects of undefined behaviour.
This was inspired by yet another long bout of explanation in a recent thread, so I thought it was time to dust off a bunch of compilers, and compare.
This is the test code.
#include <stdio.h>
int f1 ( void ) {
printf( "f1 called = 1\n" );
return 1;
}
int f2 ( void ) {
printf( "f2 called = 2\n" );
return 2;
}
int f3 ( void ) {
printf( "f3 called = 3\n" );
return 3;
}
int f4 ( void ) {
printf( "f4 called = 4\n" );
return 4;
}
int f5 ( void ) {
printf( "f5 called = 5\n" );
return 5;
}
/* Although * happens before +, there is NO guarantee that */
/* f1() will always be called after f2() and f3() */
/* http://c-faq.com/expr/precvsooe.html */
void test1 ( void ) {
int result;
printf( "Precedence is not the same as sub-expression evaluation order\n" );
result = f1() + f2() * f3() - f4() / f5();
printf( "Result=%d\n", result );
printf( "inline=%d\n", f1() + f2() * f3() - f4() / f5() );
}
/* The nature of 'before' and 'after' is poorly understood */
/* http://c-faq.com/expr/evalorder2.html */
void test2 ( void ) {
int i = 3, j = 3;
int r1 = ++i * i++;
printf( "Multiple side effects are undefined\n" );
printf( "R1=%d, i=%d\n", r1, i );
printf( "R2=%d, j=%d\n", ++j * j++, j );
}
int main ( ) {
test1();
test2();
return 0;
}
test1() gives a well-defined answer, as one would expect.
What is undefined however, is the order in which f1() to f5() are called.
That is, the order of the printf statements varies (quite amazingly).
test2() is just broken code with no redeming features. Yet despite this, people often claim they know how the result is worked out.
One set of results will however make you think about it!
All tests were done using XP as the host OS, with the exception of the gcc test, which was done under Ubuntu.
The only compiler flags used were to test the effect of optimisation.
For most of the Windows compilers, this is basically a choice between "optimise for space" or "optimise for speed".
The compiler results are presented in approximately alphabetical order.
Borland C++ Compiler 5.5, With Command Line Tools, Version 5.5.1
No Optimisation
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation level O1
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Not a lot of surprises here. The result is consistent across all three compilations and the function call order reflects what one would expect from looking at the code.
Digital Mars Compiler Version 8.42n
No Optimisation
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation level O1
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
A good level of consistency, but now the order is essentially L->R as the code is read.
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
No Optimisation
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=5
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=5
Like DMC, GCC evaluates left to right (in this test).
But the side-effect ridden evaluation of j now results in 5 and not 3.
lcc-win32 version 3.8. Compilation date: Mar 24 2008 11:52:21
No Optimisation
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation enabled
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
No great surprise here.
VC6 - Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
No Optimisation
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=3
Optimisation level O1
Precedence is not the same as sub-expression evaluation order
f5 called = 5
f4 called = 4
f3 called = 3
f2 called = 2
f1 called = 1
Result=7
f5 called = 5
f4 called = 4
f3 called = 3
f2 called = 2
f1 called = 1
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=5
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f5 called = 5
f4 called = 4
f3 called = 3
f2 called = 2
f1 called = 1
Result=7
f5 called = 5
f4 called = 4
f3 called = 3
f2 called = 2
f1 called = 1
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=5
Totally crazy!
Turning on the optimiser calls the functions in the REVERSE order.
And the grossly undefined evaluation of j in test2 returns a different answer to the unoptimised code.
Is the compiler broke? Nope, the code is just rubbish.
VS2008 - Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08
No Optimisation
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=4
Optimisation level O1
Precedence is not the same as sub-expression evaluation order
f4 called = 4
f5 called = 5
f2 called = 2
f3 called = 3
f1 called = 1
Result=7
f4 called = 4
f5 called = 5
f2 called = 2
f3 called = 3
f1 called = 1
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=4
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
Result=7
f1 called = 1
f2 called = 2
f3 called = 3
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=16, i=5
R2=16, j=4
Now we're cooking!
Imagine if you had code like this, and you upgraded your compiler from VC6 to VS2008.
Not only do you get a different order of function calls when turning on optimisation, the order is no longer the same between optimisation levels!.
And let's give a big hand to the appearance of j=4 for the first time in our results.
Open Watcom C/C++ CL Clone for 386 Version 1.7
No Optimisation
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=20, i=5
R2=20, j=3
Optimisation level O1
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=20, i=5
R2=20, j=3
Optimisation level O2
Precedence is not the same as sub-expression evaluation order
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
Result=7
f2 called = 2
f3 called = 3
f1 called = 1
f4 called = 4
f5 called = 5
inline=7
Multiple side effects are undefined
R1=20, i=5
R2=20, j=3
More variation - where will it stop?
The functions are back to being called in what seems to be precedence order.
But wait, R1=R2=20
Everything else up to now has been R1=R2=16