0

Say, in the following test expression:

int ggg9 = fggg2() + (fggg3() && fggg4() < fggg5() * fggg6());
//                 4          11         6         3

if we follow the operator precedence (shown in the line with comments below the expression) I would assume that the expression in parens will be evaluated first and then the result will be added to fggg2().

Thus I'd assume that it will be resolved in this order:

int r1 = fggg5() * fggg6(); //Precedence 3 (inside parens)
int r2 = fggg4() < r1;      //Precedence 6 (inside parens)
int r3 = fggg3() && r2;     //Precedence 11 (inside parens)
int ggg9 = fggg2() + r3;    //Outside of parens, so it's last

But x86-64 gcc 8.2 resolves it as such:

enter image description here

(I'll convert it back to C++)

    int i0;
    int r2 = fggg2();
    int r3 = fggg3();
    if(!r3) goto L13;
    int r4 = fggg4();
    int r5 = fggg5();
    int r6 = fggg6();
    int i1 = r5 * r6;
    if(r4 >= i1) goto L13
    i0 = 1;
    goto L14
L13:
    i0 = 0;
L14:
    int ggg9 = i0 + r2;

So why is the && operator seems to be evaluated before * and then < when their precedence is 11, 3, 6, respectively?

Lastly, how come it doesn't seem to care about parens by evaluating fggg2() first? VS2017 seems to be evaluating it last.

From what I see, the gcc compiler simply evaluated all those functions from left-to-right without any regard to precedence.

c00000fd
  • 20,994
  • 29
  • 177
  • 400

3 Answers3

6

You're mixing up precedence with order of evaluation.

This is a pretty common confusion. Maybe because the word "precede" in English can have a time connotation. But actually it is meant in the sense of a ranking hierarchy (e.g. here).

In the simpler expression a() + b() * c(), the precedence tells us that the operands of the * operator are b() and c(), and the operands of + are a() and the result of the multiplication. Nothing more and nothing less.

The functions a,b,c might all still be called in any order.

It is true that the value computation of * must be performed before the value computation of +, because we need to know the result of the former in order to compute the latter.

The value computation is a different step to evaluating the operands. The operands of an operator must be evaluated before its value computation, but there is not any more strict requirement than that. There is no rule about partial ordering of evaluating operands.

The left operand of + might be evaluated before the right operand of +, even if the right operand is made up of many sub-expressions. The compiler might evaluate all of the "leaf" operands, store the results in the stack, and then perform the value computations.

There is not necessarily a strict order of value computations either, e.g. in w() * x() + y() * z(), the two value computations of * may occur in either order.


In your code the && operator does have a special ordering restriction (sometimes called short circuit). The left operand must be evaluated before beginning evaluation of the right operand.

The precedence tells us that the left operand of && is fgg3(), and the right operand of && is fggg4() < fggg5() * fggg6()). So it is a requirement that fgg3() be called before any of fgg4, fgg5 and fgg6. There are no other restrictions on the order of evaluation of operands in your example. The fgg2 could occur at any time, and the 4,5,6 could be in any order so long as they are all after 3.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Thanks. Where are you taking all this from? Is there an official spec for that? (I'm referring to the "order of evaluation".) – c00000fd Dec 06 '18 at 09:12
  • 1
    @c00000fd see https://stackoverflow.com/questions/81656/where-do-i-find-the-current-c-or-c-standard-documents/ and https://en.cppreference.com/w/cpp/language/eval_order – M.M Dec 06 '18 at 22:00
  • Thanks. One question about the `&&` and `||` operators. If we look at their [precedence order](https://en.cppreference.com/w/c/language/operator_precedence) they have 11 and 12. What's the point of assigning it so low when they are clearly evaluated first after parens or so? Definitely before `*` or `<` that have lower numbers in that table. – c00000fd Dec 06 '18 at 22:17
  • @c00000fd your last comment again mixes up precedence and order of evaluation ... saying that the precedence of `&&` is lower than `*` means that in the expression `a && b * c`, the operands of `&&` are `a` and `b * c`. If the precedence were the other way around, the operands of `&&` would be `a` and `b`, and the operands of `*` would be `a&&b` and `c`. – M.M Dec 06 '18 at 22:21
  • OK, that's probably what I'm missing. Let me recap. In your example: `a && b * c` the operator `&&` has precedence of 11, while `*` has 3. So in my understanding, the operator with lower precedence will be executed first. So following that logic it should've been `x = b * c` and then `a && x`. When in reality `&&` is evaluated first by checking if `a` is 0, and if it's not, it computes `b * c` and then checks if that is not 0 and only then assigns 1 to the result, else 0. In this case the precedence value for `&&` i.e. 11 makes no sense to me. So what am I missing? – c00000fd Dec 06 '18 at 23:13
  • @c00000fd "the operator with lower precedence will be executed first" - that's not correct. The precedence does not determine the order of evaluation. I've said this several times... – M.M Dec 06 '18 at 23:23
  • Maybe a baking analogy will help? "The butter is mixed with the sugar and added to the flour" -- you must mix the butter and sugar before adding that mixture to the flour; however you could still have measured out the flour before doing any mixing – M.M Dec 06 '18 at 23:36
  • Well, ok, whatever the term for it is. Let's take the 4th comment above. (Yours.) The precedence of `&&` is 11 which is higher by number than `*`, i.e. 3. So if we follow your statement there, then it should be evaluated as `a && b` first and then `result * c`. But that's not what happens. That's what's confusing. – c00000fd Dec 06 '18 at 23:40
  • Using the numbers you linked in your third comment, the lower number means higher precedence. See the first paragraph of that page, "Operators are listed ... in descending precedence". Note that there are no "official" precedence numbers or precedence tables, the C++ Standard uses a language grammar and a precedence table is a way of presenting the language grammar in a more easily accessible form (see the "Notes" section of that link) – M.M Dec 07 '18 at 00:00
0

You're confusing order of evaluation with precedence of operators, while the precedence is guaranteed to be preserved, the order of evaluation is not, the compiler is free to choose the order.

It may evaluate fggg2() first, in the middle or between the evaluation of other operands.

Jans
  • 11,064
  • 3
  • 37
  • 45
0

You forgot to factor in the short-circuit evaluation of the && operator.

The left-hand side of the && operator always gets evaluated before the right-hand side.

In your attempt to simplify the operation, the first half of r3 must be evaluated, if it evaluates to false, none of what you call r1 and r2 will be evaluated.

Short-circuit evaluation is required in C++.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thanks for the info. Is there a spec that shows all these rules? i.e. "short-circuit evaluation of the && operator", etc. concerning evaluation order. – c00000fd Dec 06 '18 at 17:24
  • Every good C++ book will have an explanation of short circuit evaluation. The C++ specification runs to about 1500 pages. You don't want to read it. – Sam Varshavchik Dec 06 '18 at 17:30