How are logical expressions that include increment and decrement operators evaluated in C?

100 Views Asked by At
int a = 0, b= 1, c = 1;
if (c-- || ++a && b--)

I know that the precedence for && is the highest here. So what happens? Does it start from && and then looks at the expression on its left which is (c-- || ++a) and evaluates that first?

I have been experimenting for a while with different expressions and I just can't wrap my head around it. Thanks in advance.

Edit: I would have used parenthesis if I could but this is a uni question so I don't really have a say

3

There are 3 best solutions below

0
dbush On BEST ANSWER

Operator precedence does not totally determine evaluation order. What it does do is dictate how operands are grouped. And since && has higher precedence than || as you noted, your expression is equivalent to:

(c-- || (++a && b--))

Given that the && operator uses short circuit evaluation, ++a would be evaluated before ++b. However, the entire subexpression ++a && b-- is the right side of the || operator which also uses short circuit evaluation, which means the left side, i.e. c-- would get evaluated first.

Since c-- evaluates to 1, the right side of the || operator, i.e. ++a && b--, is not evaluated. So c gets decremented and a and b are left unchanged.

0
Barmar On

Because of operator precedence, this is equivalent to

if (c-- || (++a && b--))

Logical operators are processed left-to-right with short-circuiting. So this first performs c--. This decrements c to 0, but because it's post-decrement the value is its original value 1. This is truthy, so the || operator immediately evaluates to 1 without evaluating its right hand operand.

So the condition is true, but only c is modified.

0
Lundin On

I know that the precedence for && is the highest here.

Well... no. The precedence for the -- postfix operators is the highest, second highest is the prefix ++, then &&, then ||.

Operator precedence/associativity is only relevant for determining which operands that "glue" to which operator. In this case operator precedence makes the expression equivalent to (c--) || ((++a) && (b--)).

From there on we can forget about operator precedence and instead worry about order of evaluation. Normally, operators in C do not specify a an order of evaluation of the operands. Logical AND and OR are exceptions here, explicitly stating a defined order of evaluation (C17 6.5.13):

Unlike the bitwise binary & 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 equal to 0, the second operand is not evaluated.

Similar text also exists for ||. These special evaluation rules are often referred to as "short circuit evaluation", which is a rather unhelpful analogy (details: What is the meaning of "short circuit" operators?)

All of this means that the compiler has to build up an internal expression parser tree where the sub expressions inside ++a && b-- will only get evaluated if that whole expression will get evaluated, which in turn depends on the outcome of the evaluation of (c--).

So the compiler must evaluate the expression as:

  • Evaluate c--, if 1 then the result is 1, stop evaluation. The c-- is then executed.
  • Otherwise, evaluate and execute ++a. If 0, then the result is 0, stop evaluation. The c-- is then executed.
  • Otherwise, evaluate b--. If '1', then the result is 1, otherwise 0. c-- and b-- are then executed.