Home > Mobile >  Order of checking the conditions in C
Order of checking the conditions in C

Time:11-24

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 :)

CodePudding user response:

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 illustrates 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 building 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.

  • Related