-1
#include <iostream>

int main()
{
    int a = 0;
    std::cout << a++ << ' ' << ++a << ' ' << a++ << '\n';
}

This code gives me this output 2 3 0 when compiled with C++11 on ideone.

As mentioned here here I know that modifying the value of a variable more than once without an intervening sequence point will cause undefined behavior, but since C++ doesn't evaluate expressions from left to right and computers doesn't behave randomly, I would like to know how does the compiler decide which sub-expression to chose first, second, and third in the above example.

edit: I'm using Microsoft Visual Studio 2013, and the question was asked by a student and I could't explain why do we get those random results.

Community
  • 1
  • 1
Gaith
  • 798
  • 8
  • 19
  • 1
    Which compiler? Which version? Which options of that compiler? Undefined is undefined. – laune Feb 16 '14 at 20:14
  • @rici I don't think it's a duplicate because the question you have mentioned only tell that we get undefined behavior but doesn't explain why. – Gaith Feb 16 '14 at 20:26
  • 1
    @Gaith: It's undefined behaviour because the standard says it's undefined behaviour. There is no other possible explanation. – rici Feb 16 '14 at 20:37

2 Answers2

1

An individual compiler may not behave randomly (although it is allowed to with undefined behaviour) but there is no "the compiler". Different compilers will handle that expression in different ways. The same compiler will handle it in different ways if it is given different options.

The key to undefined behaviour is to avoid it, not to try to understand it. Unless you actually have some need to reverse engineer a particular compiler's quirks, there is no point in trying to second-guess a particular instance of undefined behaviour.

rici
  • 234,347
  • 28
  • 237
  • 341
0

There is a grammar for the language, and parsers follow the grammar structure when decomposing sentences of the language, according to terminals and nonterminals. The result (at least conceptually) is an abstract syntax tree, representing the original program as a tree of nodes of operations and operands. So far, everything is pretty deterministic, at least as long as similar parsing techniques are applied.

Then, code generation sets in, creating (virtual) machine statements for each operations. Again, this should be pretty deterministic.

Finally, optimization kicks in, and now all bets are off. The optimzing algorithm may, within the constraints defined by the code generated so far, decide to shuffle things around, drop useless code, combine duplicated code in alternative branches, and so on...

laune
  • 31,114
  • 3
  • 29
  • 42