Motun
Motun

Reputation: 2149

An example about operator precedences

As far as I'm aware of, unary operators have prior precedence over || and &&. In the following code, I'd expect an output where all outputs are equal to 1. Yes, there is a short-circuit but shouldn't those pre-increments calculated before || and &&? How these precedences work here?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a = 0, b = 0, c = 0;
    a = b = c == 1;
    c = ++a || ++b && ++c;    // short-circuit here
    printf("a=%d, b=%d, c=%d\n", a, b, c);
}

Output:
a=1 b=0 c=1

Upvotes: 1

Views: 114

Answers (3)

sfstewman
sfstewman

Reputation: 5667

The fact that || is short-circuiting and the lowest precedence operator explains the result. Because ++ is the higher precedence than && and && is higher precedence than ||, the expression tree for ++a || ++b && ++c is:

       ||   -- left:   ++a
            -- right:  &&   --left:  ++b
                            --right: ++c

So, to evaluate the expression, C first considers the rules for evaluating ||, given by 6.5.14 in the C11 standard. Specifically:

6.5.14.4: Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.

The fact that || is short-circuiting means that it evaluates its left operand first and only evaluates its right operand if the left is zero. So to evaluate the expression ++a || ++b && ++c, C requires the following:

  1. Evaluate ++a (a is incremented by one and the expression is equal to its incremented value). If it is non-zero, then || expression is equal to 1 and the right side is never evaluated.
  2. Otherwise, evaluate ++b and ++c, in left-to-right order. If ++b is zero, then ++c is never evaluated. If both are non-zero, then the expression is equal to 1.

Because ++a evaluates to 1, the right side of the || is never evaluated. This is why you have a==1, b==0, and c==1.

There's one additional problem with the statement c = ++a || ++b && ++c, which is that there's a potential for the statement to invoke undefined behavior. If ++a is false and ++b is true, then ++c has to be evaluated. However, there is no sequence point between c = ... and ++c. Since the expressions both modify c with no sequence point in between, the behavior is undefined. For a further explanation of this see, for example, https://stackoverflow.com/a/3575375/1430833

Upvotes: 2

erip
erip

Reputation: 16935

I think you're confusing operator precedence with order of evaluation. Here's an outline of what happens. In the comments, my use of = is that of identity - much like the mathematical equals sign.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // all variables are assigned the value 0
    int a = 0, b = 0, c = 0;

    // a and b are equal to the evaluation of `c == 1`, which is false. a and b are 0
    a = b = c == 1;

    /* 
       && has higher precedence than ||, so ++b and ++c are "grouped": ++a || (++b && ++c)
       ++a is evaluated, a = 0+1 = 1. 1 is true, short circuit the second grouping. 
       c = a = 1.
    */
    c = ++a || ++b && ++c;    // short-circuit here

    // a = c = 1, b = 0
    printf("a=%d, b=%d, c=%d\n", a, b, c);
}

Upvotes: 1

haccks
haccks

Reputation: 105992

Operator precedence doesn't have any relation with order of evaluation. Higher precedence means that grouping of operands to that operator is done first.

In the statement

c = ++a || ++b && ++c;  

the grouping/binding of the operands of ++ will be done first and then that of && and || respectively. To show this I am adding parenthesis to the expression

  • ++ has higher precedence so bind operand to it first

    c = (++a) || (++b) && (++c); 
    
  • && has higher precedence than ||

    c = (++a) || ((++b) && (++c)); 
    
  • || has higher precedence than =

    c = ((++a) || ((++b) && (++c)));   
    
  • = has least precedence of all

    (c = ((++a) || ((++b) && (++c))));   
    

Upvotes: 3

Related Questions