Evaluation of C expression
Asked Answered
B

8

6
int main() {
  int i = -3, j = 2,  k = 0, m;
  m = ++i || ++j && ++k;
  printf("%d %d %d %d\n", i, j, k, m);
  return 0;
}

i thought that && has more precedence that || as per this logic ++j should execute, but it never does and the program outputs -2 2 0 1. What is going on here? What are the intermediate steps?

Bautista answered 12/10, 2010 at 15:16 Comment(0)
A
13

&& does have higher precedence than ||, which means that ++i || ++j && ++k parses as ++i || (++j && ++k).

However this does not change the fact that the RHS of || only executes if the LHS returns 0.

Precedence does not affect order of evaluation.

Arrowhead answered 12/10, 2010 at 15:20 Comment(0)
D
5

Precedence doesn't have an effect on order of evaluation (except as necessary - some sub-expressions might need to be evaluated before others due to precedence). For example, in the simple expression:

a() + b() + c() * d()

even though multiplication has precedence over addition, the compiler is free to perform the calls to the functions in any order it likes, and might call a() or b() before or after it performs the multiplication. Obviously, it does have to evaluate c() and d() before it performs the multiplication. If these functions have side-effects (like modifying and using global variables), the indeterminate evaluation order could result in unexpected outcomes.

However, for some operators, the standard does prescribe a strict order of evaluation. It this to say about the || logical or operator:

Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.

So not only does the || provide an ordering guarantee, it also guarantees that under certain conditions, the 2nd operand won't be evaluated at all.

(it also says something similar for the && - except that in that case the 2nd operand isn't evaluated if the first evaluates to 0. But in your example, the || comes first).

Other operators that provide some guarantee of ordering include the comma operator and the function call (which guarantees that arguments have been evaluated, but not the order in which those arguments have been evaluated).

Defoe answered 12/10, 2010 at 15:20 Comment(1)
Nice explanation. I'd add that there's a good reason for short-circuit evaluation -- it makes conditionals much easier to write. For example, if(i < ARRAY_MAX && array[i] == foo) or if(bar && bar->baz != MAGIC_NUMBER). You know the first part (aka the guard) is evaluated first, so you won't read outside the array or dereference NULL. Without a guaranteed order, you would have to nest two if statements. It some other languages, the order isn't guaranteed, so you see nested if statements used as a guard all the time.Fredkin
K
4

C++ uses lazy evaluation for logical operators.
If you write a || b, and a is true, b will never evaluate, since the result will be true even if b is false.
Similarly, a && b will not evaluate b if a is false.

Since ++i evaluates to a truthy value, none of the other expressions are evaluated.

Kilby answered 12/10, 2010 at 15:18 Comment(4)
C++ uses lazy evaluation... a bit too broad a statement. For example 0 & x is always 0 but x is nonetheless evaluated. This laziness, or, more correctly, short-circuit evaluation refers only to built-in operators && and || (and in some sense to ?:)Philately
@sje397: Because truth (the result of the || clause) is 1.Kilby
@sje397: because the boolean expression overall evaluates to true, which yields 1 when converted to intPhilately
m is 1 because the expression evaluates to true and in C true = 1.Fallal
P
1

&& and || use short-circuit evaluation, i.e. in the expression a&&b a is evaluated first, if it is false then the whole expression is false and b is not evaluated. In a||b if a is true then b is not evaluated. Note that if you overload && or || short-circuit rules will no longer apply. HTH

Philately answered 12/10, 2010 at 15:19 Comment(0)
B
1

C does short-circuiting of logical expressions, so evaluation of ++i is enough to figure out that m should be true.

Borisborja answered 12/10, 2010 at 15:20 Comment(0)
C
1
  1. The || operator forces left-to-right evaluation, so the expression ++i is fully evaluated first, with a result of -2.
  2. The || operator forces a sequence point, so the side effect is applied and i is now equal to -2.
  3. The result of the expression ++i is not 0, so the expression ++j && ++k is not evaluated at all.
  4. Since the LHS is non-zero, the result of the entire expression ++i || ++j && ++k is 1 (true), which is assigned to m.

Just to echo what several others have said, precedence and order of evaluation are not the same thing.

Calcic answered 12/10, 2010 at 18:11 Comment(1)
+1 - the only answer so far that answers all of the question.Riccardo
A
0

Were you by chance actually wanting to type:

m = ++i | ++j & ++k;

which outputs -2 3 1 -1

Airliner answered 12/10, 2010 at 15:20 Comment(2)
no, FWIW i know the difference between bitwise operators and other operators.Bautista
I figured you did, so wasn't trying to be presumptuous :) I was tutoring someone the other day who had them confused.Airliner
B
0

m = ++i || ++j && ++k;

Since && has higher precedence than || so the expression is interpreted as ++i || (++j && ++k)

|| is short circuiting and so right hand operand of || operator doesn't get evaluated because ++i returns a non zero value.

Boylan answered 12/10, 2010 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.