Short circuit and operator precedence in C

You're conflating two related but different topics: operator precedence and order of evaluation.

The operator precedence rules dictate how various operators are grouped together. In the case of this expression:

 a=0 && --b;

The operators are grouped like this:

 a = (0 && (--b));

This has no effect however on which order the operands are evaluated in. The && operator in particular dictates that the left operand is evaluated first, and if it evaluates to 0 the right operand is not evaluated.

So in this case the left side of && which is 0 is evaluated, and because it is 0 the right side which is --b is not evaluated, so b is not incremented.

Here's another example of the difference between operator precedence and order of evaluation.

int val()
{
    static x = 2;
    x *= 2;
    return x;
}

int main()
{
    int result = val() + (5 * val());
    printf("%d\n", result);
    return 0;
}

What will the above program print? As it turns out, there are two possibilities, and both are valid.

In this expression:

val() + (5 * val())

There are no operators that have any type of short circuit behavior. So the compiler is free to evaluate the individual operands of both + and * in any order.

If the first instance of val() is evaluated first, the result will be 4 + ( 5 * 8) == 44. If the second instance of val() is evaluated first, the result will be 8 + (5 * 4) == 28. Again, both are valid since the operands may be evaluated in any order.


Precedence affects how ambiguous expressions are parsed. When there are multiple ways to interpret an expression with several operators, precedence tells us which interpretation is correct. Think of precedence as a mechanism to figure out where the implied parentheses are.

For example in the statement in question there are two valid ways to parse it. If = had higher precedence than && it could be read as:

(a = 0) && --b;

But since && has higher precedence, it's actually interpreted as:

a = (0 && --b);

(Note: Your code's formatting suggests it's the first. Be careful not to mislead!)

Evaluation order is different from precedence. They're related, but independent concepts. After precedence is used to determine the correct parsing of an expression, evaluation order tells us the order to evaluate the operands in. Is it left to right? Right to left? Simultaneous? Unspecified?

For the most part evaluation order is left unspecified. Operators like + and * and << have no defined evaluation order. The compiler is allowed to do whatever it likes, and the programmer must not write code that depends on any particular order. a + b could evaluate a then b, or b then a, or it could even interweave their evaluations.

= and &&, among others, are exceptions. = is always evaluated right to left, and && is left to right with short circuiting.

Here's how evaluation proceeds step-by-step for our statement:

  1. a = (0 && --b), = evaluated right to left
    1. 0 && --b, && evaluated left to right with short circuiting
      1. 0, evaluates false which triggers short circuiting and cancels the next step
      2. --b, not evaluated due to short circuiting
      3. result is 0
    2. a, variable reference evaluated
    3. a = 0, assignment occurs and overall result is 0

You said that there is no specific order for + and *, but this table shows the order to be left to right. Why so?

The last column of that table is associativity. Associativity breaks precedence ties when we use the same operator twice, or when we use operators with the same precedence.

For example, how should we read a / b / c. Is it:

  • (a / b) / c, or
  • a / (b / c)?

According to the table / has left-to-right associativity, so it's the first.

What about chained assignments like foo = bar = baz? Now, assignment has right-to-left associativity, so the correct parsing is foo = (bar = baz).

If this all gets confusing, focus on one simple rule of thumb:

"Precedence and associativity are independent from order of evaluation."