CS253: Software Development with C++

Spring 2021

Order Of Evaluation

Show Lecture.OrderOfEvaluation as a slide show.

CS253 Order Of Evaluation

made at imgflip.com

Basic Expressions

Consider the expression a+b, which parses as:

        			 ┌───┐
        			 │ + │
        			 └───┘
        			 ⠌   ⠡
        			⠌     ⠡
        		    ┌───┐     ┌───┐
        		    │ a │     │ b │
        		    └───┘     └───┘

Basic Expressions

How does a+b*c parse?

        	  ┌───┐			      ┌───┐
        	  │ * │			      │ + │
        	  └───┘			      └───┘
        	  ⠌   ⠡			      ⠌	  ⠡
        	 ⠌     ⠡		     ⠌	   ⠡
             ┌───┐     ┌───┐		 ┌───┐	   ┌───┐
             │ + │     │ c │		 │ a │	   │ * │
             └───┘     └───┘		 └───┘	   └───┘
             ⠌	 ⠡				   ⠌   ⠡
            ⠌	  ⠡				  ⠌	⠡
        ┌───┐	  ┌───┐			      ┌───┐	┌───┐
        │ a │	  │ b │			      │ b │	│ c │
        └───┘	  └───┘			      └───┘	└───┘

The right-hand side, of course. * has higher precedence than +.

Basic Expressions

Let’s use parentheses. How does (a+b)*c parse?

        	  ┌───┐			      ┌───┐
        	  │ * │			      │ + │
        	  └───┘			      └───┘
        	  ⠌   ⠡			      ⠌	  ⠡
        	 ⠌     ⠡		     ⠌	   ⠡
             ┌───┐     ┌───┐		 ┌───┐	   ┌───┐
             │ + │     │ c │		 │ a │	   │ * │
             └───┘     └───┘		 └───┘	   └───┘
             ⠌	 ⠡				   ⠌   ⠡
            ⠌	  ⠡				  ⠌	⠡
        ┌───┐	  ┌───┐			      ┌───┐	┌───┐
        │ a │	  │ b │			      │ b │	│ c │
        └───┘	  └───┘			      └───┘	└───┘

The left-hand side, of course. Parentheses mean “do this first”.

No

NO!

Really

              ┌───┐
              │ * │
              └───┘
              ⠌	  ⠡
             ⠌	   ⠡
         ┌───┐	   ┌───┐
         │ + │	   │ c │
         └───┘	   └───┘
         ⠌   ⠡
        ⠌     ⠡
    ┌───┐     ┌───┐
    │ a │     │ b │
    └───┘     └───┘

Really

              ┌───┐
              │ * │
              └───┘
              ⠌	  ⠡
             ⠌	   ⠡
         ┌───┐	   ┌───┐
         │ + │	   │ c │
         └───┘	   └───┘
         ⠌   ⠡
        ⠌     ⠡
    ┌───┐     ┌───┐
    │ a │     │ b │
    └───┘     └───┘

Possible orders of evaluation:
a, b, c, +, *
a, b, +, c, *
a, c, b, +, *
b, a, c, +, *
b, a, +, c, *
b, c, a, +, *
c, a, b, +, *
c, b, a, +, *

Why is this difficult?

              ┌───┐
              │ * │
              └───┘
              ⠌	  ⠡
             ⠌	   ⠡
         ┌───┐	   ┌───┐
         │ + │	   │ c │
         └───┘	   └───┘
         ⠌   ⠡
        ⠌     ⠡
    ┌───┐     ┌───┐
    │ a │     │ b │
    └───┘     └───┘

Some Operators Are Different

A user-defined operation (e.g., operator+) does not have order determined, because it’s a function called with two arguments. The order of evaluation of function arguments is unspecified.

Short-circuiting operators

int n = time(nullptr) > 40 ? 2024'04'26 : sleep(60*60);
cout << n;
20240426

Pre-/Post-Increment/Decrement

This example is bad. Don’t do this:

int n = 1;
cout << ++n * ++n << endl;
c.cc:2: warning: operation on 'n' may be undefined
9
Why not?

n is being modified twice in this expression, and the order of those modifications is not unspecified.

Another bad example

Don’t do this, either:

short nums[] = {1, 2, 3, 4};

short *p = nums;
cout << *p++ << ' ';
cout << *p++ << '\n';

p = nums;
cout << *p++ << ' ' << *p++;
c.cc:8: warning: operation on 'p' may be undefined
1 2
1 2
Why not?

cout << *p++ << ' ' << *p++; is an expression, and p is being modified twice in that expression. The order of the various parts is unspecified. << is just another operator, like +, =, or /=.

A non-increment example

At this point, students think “Ah—increment is unpredictable. I’ll avoid it.” That’s not the only potential problem.

// Return increasing numbers: 0, 1, …
int inc() {
    static int n = 0;
    return n++;
}

int main() {
    int a[] = {inc(), inc(), 33, 44}; // 0,1,33,44 or 1,0,33,44?
    a[inc()] = inc();                 // a[2] = 3 or a[3] = 2?
    for (int n : a)
        cout << n << ' ';
}
0 1 33 2 

Note the lack of any sort of warning.

Why not?

Why not? Because, in all cases above, the variable is being modified twice in the same expression.

Another Example

int a() { cout << 'A'; return 1; }
int b() { cout << 'B'; return 2; }

int main() {
    cout << a()+b() << '\n';    // Might display AB3 or BA3.
    cout << a() << b() << '\n'; // Might display A1B2, AB12, BA12
    return 0;
}
AB3
A1B2

Experiments don’t matter

You cannot determine the “correct” result by experimentation.

It’s like when your grandmother asks you what kind of music you kids like these days. You certainly don’t represent everybody in your age group. Besides, your own tastes may change.

How to do it right

Break expressions apart, perhaps using explicit temporaries, to control the order of evaluation:

int a() { cout << 'A'; return 1; }
int b() { cout << 'B'; return 2; }

int main() {
    int c = a();    // Will display A
    c += b();       // Will display B
    cout << a();    // Will display A1
    cout << b();    // Will display B2
    return 0;
}
ABA1B2