III_phr
III_phr

Reputation: 76

Order of checking the conditions in C

So I was reading about the order of different operators, and I read that && has higher importance than || and it would evaluate sooner (source). Then somebody asked a question about what this piece of code would print:

#include <stdio.h>
int main(){
    int a=0, b=0, c=0, d=0;
    if(a++>0 || ++b==1 || c--<=0 && d++>c--){
        printf("if\na:%d\nb:%d\nc:%d\nd:%d\n",a,b,c,d);
    }
    else{
        printf("else\na:%d\nb:%d\nc:%d\nd:%d\n",a,b,c,d);
    }
    return 0;
}

And I thought that the c-- <= 0 && d++ > c-- would evaluate first, which is true in total. after the process, c would be equal to -2 and d would be equal to 1. Then it would start checking from the left side, evaluating a++ > 0 || ++b == 1 which is true, a would be 1 at the end and b is 1 in the condition and after that. so the total condition would be true || true and it is true, so we will print:

if
a:1
b:1
c:-2
d:1

Yes? Apparently, no. I've tested it with GCC (Mingw) on my system (Windows 10), and with an online compiler (this one) and both printed:

if
a:1
b:1
c:0
d:0

I've changed the condition into this: if(a++>0 || ++b==1 || (c--<=0 && d++>c--) ) but the output is the exact same thing on both places. Is there something that I don't pay attention to? Or is this something like a bug? It almost looks like that || and && have the same priority and the whole thing is evaluated from the left side, and short-circuits occurs and other things. If I change the ++b==1 part into ++b==0, then the output is the same as I predicted.
Thanks in advance for any kind of help :)

Upvotes: 0

Views: 819

Answers (1)

Steve Summit
Steve Summit

Reputation: 47923

The expression in this question:

if(a++>0 || ++b==1 || c--<=0 && d++>c--)

is a classic example of a horrible, horrible expression, outrageously unrealistic and impractical, and punishingly difficult to understand, which nevertheless does a good job of illustrating a super important point: precedence is not the same as order of evaluation.

What precedence really tells us is how the operators are hooked up with their operands. So given the simplified expression

A || B || C && D

which two subexpressions do the first ||, and the second ||, and the && actually tie together and operate on? If you're a compiler writer, you answer these questions by constructing a "parse tree" which explicitly shows which subexpression(s) go with which operators.

So, given the expression A || B || C && D, does the parse tree for the expression look like this:

        &&
       /  \
     ||    D
    /  \
  ||    C
 /  \
A    B

or like this:

  ||
 /  \
A    ||
    /  \
   B    &&
       /  \
      C    D

or like this:

      ||
     /  \
    /    \
  ||      &&
 /  \    /  \
A    B  C    D

To answer this, we need to know not only that the precedence of && is higher than ||, but also that || is left-associative. Given these facts, the expression

A || B || C && D

is parsed as if it had been written

(A || B) || (C && D)

and, therefore, results in the third of the three candidate parse trees I showed:

      ||
     /  \
    /    \
  ||      &&
 /  \    /  \
A    B  C    D

But now we're in a position to really see how the "short circuiting" behavior of the || and && operators is going to be applied. That "top" || is going to evaluate its left-hand side and then, if it's false, also evaluate the right-hand side. Similarly, the lower || is going to evaluate its left-hand side. So, no matter what, A is going to get evaluated first. For the expression in the original question, that corresponds to a++ > 0.

Now, a++>0 is false, so we're going to have to evaluate B, which is ++b == 1. Now, that is true, so the result of the first || is "true".

So the result of the second (top) || operator is also "true".

So the right-hand side of the top || operator does not have to be evaluated at all.

So the entire subexpression containing && will not be evaluated at all.

So even though && had the highest precedence, it ended up getting considered last, and (since the stuff to the left involved || and was true) it did not end up getting evaluated at all.

The bottom line, as I started out by saying, is that precedence does not determine order of evaluation.

Also, if it wasn't said elsewhere, this guaranteed, left-to-right behavior is only guaranteed for the || and && operators (and, in a different way, for the ternary ?: operator). If the expression had been

A + B + C * D

it would not have been true that, as I said earlier, "no matter what, A is going to get evaluated first". For arithmetic operators like + and *, there's no way to know whether the left-hand side or the right-hand side is going to get evaluated first.

Upvotes: 4

Related Questions