c00000fd
c00000fd

Reputation: 22327

Confusion over operator precedence in C++

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.

Upvotes: 0

Views: 397

Answers (3)

Jans
Jans

Reputation: 11250

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.

Upvotes: 0

M.M
M.M

Reputation: 141658

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.

Upvotes: 6

Sam Varshavchik
Sam Varshavchik

Reputation: 118435

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++.

Upvotes: 0

Related Questions