slow_hare's blog

By slow_hare, history, 3 years ago, In English

Could anyone please explain how these expressions are evaluated?

#include <bits/stdc++.h>
using namespace std;

int main()
{
  int a = 1;
  a = a++ + a++;
  cout<<a<<endl;
  a = 1;
  a = ++a + ++a;
  cout<<a<<endl;
  a = 1;
  a = a++ + ++a;
  cout<<a<<endl;
  a = 1;
  a = ++a + a++;
  cout<<a<<endl;
  
  return 0;
}

OUTPUT:

3

6

4

5

  • Vote: I like it
  • +16
  • Vote: I do not like it

| Write comment?
»
3 years ago, # |
Rev. 2   Vote: I like it +52 Vote: I do not like it

In short: don't. Don't write such code, it's undefined behavior.

Perhaps the easiest way to understand the idea is 'sequence points'. Sequence points split the code into parts which all get evaluated one by one in a well-specified order. For example, in x++, x *= 2, , is a sequence point so first x++ gets evaluated and then x *= 2. Same with && and || operators: first the left side gets executed, then the right one (if needed).

On the other hand, + does not specify a sequence point, so the standard doesn't say if the left side should be run first or the right side. So when you do a++ + ++a, the compiler doesn't understand what order you want: do you want a++ to modify a and then use its value to evaluate ++a, or the other way round? Because of this uncertainity, such code is considered invalid, which the standard does by saying 'this code has undefined behavior'. The compiler is allowed to do anything when it encounters undefined behavior. When I say anything, I mean really anything, it can crash, format your hard drive, cause a nuclear explosion or whatever it can do.

Another nice example of undefined behavior is i = ++i + 1. The standard doesn't say that = is a sequence point. And you modify i on both sides of assignment: on the left i = is an assignment, on the right ++i is a pre-increment (i.e. assignment), so it's UB.

This changed a bit in C++11. In C++11 and later, there are no sequence points anymore, but there are sequence relations which are similar to points. (If you don't dive into details, sequence points always specify 1-2-3-4... order and sequence relations denote a DAG and the compiler may choose any topological sort order it likes, if there are different variants.) Starting with C++11, the second example is no longer UB, because there's a relation that assignment is performed after both sides are evaluated, so first ++i + 1 is evaluated and then i = is done. It doesn't mean that you should write such code in production, though.