LocalHost
LocalHost

Reputation: 910

Short circuit and operator precedence in C

I know that logical operators in C follow short circuiting but my doubt is that are short circuiting and operator precedence rules not opposing each other. See the below example :

#include<stdio.h>
int main()
{
    int a;
    int b=5;

    a=0 && --b;
    printf("%d %d",a,b);

    return 0;
}

According to the precedence rules, the highest precedence is of the prefix operator. So --b should be evaluated first and then the && and at last result will be assigned to a. So expected output should be 0 4. But in this case the second operand of && never actually executes and result comes out to be 0 5.

Why precedence rules are not being applied here. Are logical operators exempted from precedence rules? If yes, what other operators show such behavior? And what is the logic behind this behavior?

Upvotes: 8

Views: 1728

Answers (5)

dbush
dbush

Reputation: 225767

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.

Upvotes: 8

anastaciu
anastaciu

Reputation: 23832

operator && 's order of evaluation is left to right.

= has lower precedence, in fact only ooperator , has lower precedence than =.

So the expresssion will read a = (0 && --b) being 0 evaluated first given the mentioned order of evaluation.

Since 0 evaluates to false, there is no need to evaluate the second part of the expression because false && true is false, given the first part of the expression is false, the expression will always be false.

If you had || operator the second part of the expression would have to be evaluated.

Upvotes: 0

John Kugelman
John Kugelman

Reputation: 362157

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

Upvotes: 8

Marc Balmer
Marc Balmer

Reputation: 1830

Operator precedence is not all that plays in the game. There is also order of evaluation. And that mandates that a=0 is evaluated first (evaluation order is from left to right), and then right part after the && is not evaluated at all.

That is how C works.

Upvotes: -2

DarkAtom
DarkAtom

Reputation: 3171

Operator precedence doesn't necessarily tell that an expression gets executed first, it just means that the expression is parsed such that the result of the higher-precedence operation is used in the lower-precedence operation and not the other way around. The actual expressions only get evaluated if they need to!

Upvotes: 0

Related Questions