-1

The book I use gives associativity of logical operators as right to left, so I expect the result of this code to be 2 2 1 but it is 2 1 1.

int x,y,z;
x=y=z=1;
z=++x||++y||++z;
printf("%d %d %d",x,y,z);

Why is this?

Dave
  • 44,275
  • 12
  • 65
  • 105
chinmay
  • 850
  • 2
  • 8
  • 16
  • possible duplicate of [How does C++ handle &&? (Short-circuit evaluation)](http://stackoverflow.com/questions/5211961/how-does-c-handle-short-circuit-evaluation) –  Aug 04 '13 at 16:04
  • 2
    Associativity != operator precedence != order of evaluation. –  Aug 04 '13 at 16:04
  • 1
    @H2CO3 I think that the problem here are the keywords, if you are not aware of what the right keywords are ... you just ask – user2485710 Aug 04 '13 at 16:05
  • 1
    In defense of the OP, the search function isn't that helpful. Searching for "associative logical operators" produces nothing about short circuiting behavior, and it's not entirely obvious what to search for to produce the answer the OP is looking for. – Charles Salvia Aug 04 '13 at 16:06
  • Why did you expect `2 2 1`? I can understand expecting `2 2 2` but `2 2 1` makes no sense to me… – Dave Aug 04 '13 at 16:06
  • @user2485710 Googling "C boolean operators unexpected result" should not hurt, should it. Even if one doesn't know that this is called "short-circuiting". –  Aug 04 '13 at 16:07
  • Oh I get it; this is actually a different question about the direction of comparison. – Dave Aug 04 '13 at 16:08
  • Google is lately becoming more and more annoyingly relaxed when searching for highly specific terms. Now they're even using "bitwise" and "boolean" as interchangeable terms: http://www.google.com/#sclient=psy-ab&q=C+boolean+operators+unexpected+result&oq=C+boolean+operators+unexpected+result&gs_l=hp.3...3332.3332.2.3551.1.1.0.0.0.0.46.46.1.1.0....0...1c.1.23.hp..2.0.0.QHvFNZi0AEc&psj=1&bav=on.2,or.r_qf.&bvm=bv.50165853,d.dmg&fp=f6b247351107236d&biw=1181&bih=775 – Charles Salvia Aug 04 '13 at 16:09
  • @CharlesSalvia http://www.google.co.uk/?q=C+%22boolean%22+operators+unexpected+result – Dave Aug 04 '13 at 16:12
  • @Dave, yes I'm aware of the quote operator, but boolean and bitwise are hardly interchangeable – Charles Salvia Aug 04 '13 at 16:12
  • This answer does not address the first clause of the question, “The book I use gives associativity of logical operators as right to left”. Knowing what we do about C evaluation, we can reason that associativity would not alter the result. However, to somebody asking this question, this false statement about associativity combined with other misunderstandings could interfere with being able to figure out or understand the behavior. – Eric Postpischil Aug 04 '13 at 17:02

2 Answers2

6

|| has a short-circuit evaluation requirement. If the first operand is different than 0, then the second operand is not evaluated.

(C11, 6.5.14 Logical OR operator) p4 "If the first operand compares unequal to 0, the second operand is not evaluated."

In your case ++y and ++z are never evaluated.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • I think there is also an implicit cast to explain as z = ++x alone should make "2 1 2". – lossleader Aug 04 '13 at 16:08
  • @lossleader the `||` operator is a logical operator and yields `0` or `1`. – ouah Aug 04 '13 at 16:15
  • @ouah I guess I should have said cast-like behavior. The other point of confusion seemed to be that || does not return its first non-zero operand. – lossleader Aug 04 '13 at 16:29
2

This is a subtle topic. There are 2 types of ordering; evaluation order and associativity. Now it turns out that for && and || in C, both evaluation and associativity are left-to-right, so your book is wrong. Although the associativity in this case makes no actual difference.

To explain associativity, a || b || c, if || is right-to-left, is seen as a || (b || c). But if || is left-to-right, it is seen as (a || b) || c. Well, both produce the same results in all cases. So the associativity of boolean operators doesn't matter (of course, it does matter for some other operators: (a - b) - c != a - (b - c).

The evaluation order is different; it tells us which order to compare things in after we have applied the implicit brackets from associativity. So with left-to-right evaluation order, a || (b || c) (and (a || b) || c) is evaluated in the order a, then b, then c. With right-to-left, both will be evaluated in order c, b, a.

That means even with right-to-left evaluation, you wouldn't see 2 2 1 but rather 1 1 2. Again regardless of associativity. (edit: actually you would see 1 1 1, because you're setting z to the result of the expression, which is of course true)

Another interesting note is that for most operators, evaluation order actually isn't defined. That means (a ++) - (a ++) is undefined behaviour (thankfully; it would be a nightmare of obfuscation otherwise)

See http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence

or for a less wikipedia page, http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm

Also rule 6 here: http://en.cppreference.com/w/cpp/language/eval_order#Rules (I'm sure it's in the standard but I can only find references for C++03, not C)

Dave
  • 44,275
  • 12
  • 65
  • 105
  • I am in doubt that once we use say right to left associativity then ++x||(++y||++z) is to be evaluated so ++y is evaluated. Then the short circuit is defined on first operand only so (++x||1) would cause even ++x to be evaluated plz tell me where I am wrong – chinmay Aug 04 '13 at 17:31
  • slightly weird example with the minus and the negative mixed in there 3 - 5 - 2 === 3 + (-5 - 2) === (3 - 5) - 2; the subtlety is there because you are mixing the precedence and the right-association of the negative operator with that of the minus operator. Just found that to be weird. Because irrespective of left or right association of the minus operator the parser will not make the mistake of parentheses the way you did – Ahmed Masud Aug 04 '13 at 17:51
  • @AhmedMasud I just wanted to demonstrate a situation where associativity would make an obvious difference. The order of evaluation makes no difference, because I explicitly put brackets around both expressions. Anyway it was just a vague example. – Dave Aug 04 '13 at 17:57
  • @chinmay it may be right-to-left associativity, but if it's still left-to-right evaluation order, `++x` will run, and the rest short-circuited. If it's right-to-left, `++y||++z` will run, which in turn will cause `++z` to run, which will short-circuit `++y`, and then short-circuit `++x`. No matter what associativity or evaluation order you choose, `++y` will never run (assuming both `++x` and `++z` return non-zero) – Dave Aug 04 '13 at 17:59
  • Oh and I suppose I should make clear that I'm assuming it would still have short-circuit behaviour, so with a right-to-left evaluation order, `(x++)||1` would never run `x++`, since `1` runs first and is true. With left-to-right, `x++` would always run, and `1` would only "run" if `x++` returned 0. Since there's only one `||` in the expression, the associativity doesn't even enter into that example. – Dave Aug 04 '13 at 18:02