Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 731
LocalHost Avatar asked May 12 '20 11:05

LocalHost


People also ask

What are short circuit operators in C?

Like C, Perl provides the && (logical AND) and || (logical OR) operators. They evaluate from left to right (with && having slightly higher precedence than ||) testing the truth of the statement.

What is operator precedence short answer?

An operator precedence is the order that an operator is executed. For example, many times a multiplication operator will execute before the addition operator.

Which operator has lowest precedence in C?

4) Comma has the least precedence among all operators and should be used carefully For example consider the following program, the output is 1. See this and this for more details.

What is operator precedence in C?

Operators Precedence in C - Operator precedence determines the grouping of terms in an expression and decides how an expression is evaluated. Certain operators have higher precedence than

What is short circuiting in C++?

Short circuiting is a functionality that skips evaluating parts of a (if/while/...) condition when able. In case of a logical operation on two operands, the first operand is evaluated (to true or false) and if there is a verdict (i.e first operand is false when using &&, first operand is true when using ||) the second operand is not evaluated.

What is short circuit in Java logical operators?

In Java logical operators, if the evaluation of a logical expression exits in between before complete evaluation, then it is known as Short-circuit. A short circuit happens because the result is clear even before the complete evaluation of the expression, and the result is returned.

What is precedence and associativity in C++?

Precedence and associativity are independent from order of evaluation. The standard itself doesn't specify precedence levels. They are derived from the grammar. In C++, the conditional operator has the same precedence as assignment operators, and prefix ++ and -- and assignment operators don't have the restrictions about their operands.


2 Answers

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.

like image 139
dbush Avatar answered Nov 15 '22 00:11

dbush


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

like image 44
John Kugelman Avatar answered Nov 14 '22 23:11

John Kugelman