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

kvprajapati commented: Excellent. You are absolutely right. +10
yellowSnow commented: Very good post and interesting results - you mad scientist you! +4
Dave Sinkula commented: Thanks for the effort of running it for all those compliers and settings and posting the results. +24
mvmalderen commented: Nice posting! I agree with the previous comments. +18

Recommended Answers

All 5 Replies

Thank you very much. Very good explanation of a burning issue - undefined behaviour .

actually this could not be termed as unexplained behaviour.it could very well be explained as follows ,consider the following example.
[#include<stdio.h>
void main()
{
int i = 1;

printf( "%d %d",++i,i++ );

}
]
the output would be as follows
3 ,1
the explanation to the above is as follows
1---the value at runtime for ++i would be 2 and therefore 3 for ++i.
however the compiler when logically placing the value in stack places it as last in first out that is from left to right and so assignment of i++ to 3 .

Use code tags, it make's you post look nicer.

Don't use void main() ever again except when telling other people to not to use void main()! Use int main(),int main(void) or int main(int argc, char *argv[]).

however the compiler when logically placing the value in stack places it as last in first out that is from left to right and so assignment of i++ to 3 .

The compiler doesn't have to use any order according to the ISO C99 standard. Read this.

> actually this could not be termed as unexplained behaviour
I'm assuming that you didn't actually bother to read my post at all, or any of the links posted therein.

Statements like "My compiler does...." are meaningless.
Read the FAQs, and better yet, the standards.

If you write code like that, you're in for one hell of a surprise at some point - just check my results - utter chaos.

theres a mixup of different concepts here .function argument evaluation order isnt undefined but unspecified.

only relying on any particular order is a mistake. for ex. in test1() the computed result is always the same cuz the return values are independent of order. the behavior of test1() is well defined ,its just that the order of subex func printf() calls isnt fixed. even if the result value varied it would technically be ok (just maybe not intended). but it isnt allowed to "fail", which ++i * i++ is.

the return value of malloc() is also unspecified and can vary between different invocations on the same machine with the same program

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.