Search code examples
c++gccvisual-studio-2017operator-precedence

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.


Solution

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