Show Lecture.OrderOfEvaluation as a slide show.
Consider the expression a+b
, which parses as:
┌───┐ │ + │ └───┘ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ │ a │ │ b │ └───┘ └───┘
How does a+b*c
parse?
┌───┐ ┌───┐ │ * │ │ + │ └───┘ └───┘ ⠌ ⠡ ⠌ ⠡ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ + │ │ c │ │ a │ │ * │ └───┘ └───┘ └───┘ └───┘ ⠌ ⠡ ⠌ ⠡ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ a │ │ b │ │ b │ │ c │ └───┘ └───┘ └───┘ └───┘
The right-hand side, of course. *
has higher precedence than +
.
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!
┌───┐ │ * │ └───┘ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ │ + │ │ c │ └───┘ └───┘ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ │ a │ │ b │ └───┘ └───┘
+
node feeds to the *
node, so +
must execute before *
.
c
before the addition, or after.
┌───┐ │ * │ └───┘ ⠌ ⠡ ⠌ ⠡ ┌───┐ ┌───┐ │ + │ │ 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, +, *
a
, b
, and c
are fetched.
&& ||
,
(in an expression, not as an argument separator)
?:
+ - * / %
<< >> & | ^
< > <= >= == !=
= += -= *= /= %= <<= >>= &= |= ^=
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.
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
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
Why not? Because, in both cases, the variable is being modified twice in the same expression.
<<
is an operator, just like +
is.
cout << a << b
is one expression.
(cout << a) << b
operator<<(operator<<(cout,a), b)
&&
||
?:
,
(the binary comma operator, not function arguments)
int a() { cout << 'A'; return 1; } int b() { cout << 'B'; return 2; } int main() { cout << a()+b() << '\n'; // Might print AB3 or BA3. cout << a() << b() << '\n'; // Might print A1B2, AB12, BA12 return 0; }
AB3 A1B2
You cannot determine the “correct” result by experimentation.
Break apart expressions, perhaps using explicit temporaries, to determine the order of evaluation of sub-expressions:
int a() { cout << 'A'; return 1; } int b() { cout << 'B'; return 2; } int main() { int c = a(); // Will print A c += b(); // Will print B cout << a(); // Will print A1 cout << b(); // Will print B2 return 0; }
ABA1B2