CS253: Software Development with C++

Spring 2020

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.

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

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++ << '\n';
c.cc:8: warning: operation on 'p' may be undefined
1 2
1 2

A non-increment example

At this point, students think “Ah—increment is unpredictable. I’ll avoid it.” That would be incorrect.

// Return 1 or 0, alternating with each call.
int foo() {
    static int n = 0;
    n = 1 - n;
    return n;
}

int main() {
    int a[3] = {foo(), foo(), 333};
    a[foo()] = foo();           // a[0] = 1 or a[1] = 0?
    cout << a[2] << '\n';
}
333

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