4
x = y = z = 1;
z = ++x||++y&&++z;

operator precedence is as follows --

(pre-increment) > && > ||

so answer should be--

1.  2||2 && 2
2.  2||1
3.  1

print x,y,z should be 2,2,1

but, answer is 2,1,1.

phoenix
  • 644
  • 10
  • 22

3 Answers3

12

Precedence is not the same as order of evaluation. Precedence simply determines what operands and operators belong together. The exact order of evaluation is unspecified except with the logical operators, which are evaluated in strict left-to-right order to enable short-circuiting.

So because && has higher precedence, the variables are first grouped as follows:

(++x) || (++y && ++z)

Then, following left-to-right order, ++x is evaluated. Given that ++x is true, it is known that the entire expression is true. So expression evaluation is short-circuited. (++y && ++z) never gets evaluated. Hence y and z never get incremented.

verbose
  • 7,827
  • 1
  • 25
  • 40
8

Expressions with logical operators && and || evaluate left to right:

C99, Section 6.5.14-4 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.

Since x++ is not zero, the expression short-circuits evaluation of everything to the right of ||, including their side effects. That's why only x++ is evaluated, so x becomes 2. The remaining variables stay at 1, as they should.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Since && has a higher precedence, it would be evaluated _first,_ no? So the ++y and ++z should execute. – paxdiablo Aug 02 '13 at 02:06
  • @paxdiablo you might be right about the precedence, but I agree it's not UB – aaronman Aug 02 '13 at 02:08
  • @aaronman, I suggest you look into Annex C of the standard, it's clear on what the sequence points are. – paxdiablo Aug 02 '13 at 02:09
  • Exactly, thats what happening.! I)so,, correct me if i am wrong, once short circuit of evaluation has happened , rest part is left as it is? II) can you give a couple of examples in which this short circuit evaluation, or in other words, "condition for short circuit of evaluation"? – phoenix Aug 02 '13 at 02:11
  • @paxdiablo then you should correct the answer in the [exact duplicate](http://stackoverflow.com/questions/7521607/precedence-of-logical-operators-in-c) because it says nothing about UB – aaronman Aug 02 '13 at 02:11
  • 1
    @aaron, that's because it's _not_ an exact duplicate. There is no assignment to `c/z` in that question, hence it's _not_ UB. This question, with the assignment, is UB. – paxdiablo Aug 02 '13 at 02:14
  • @paxdiablo the printf statement prints out a,b and c implying that a was assigned even if they didn't write it that way, I think I understand the UB after the standards quote btw – aaronman Aug 02 '13 at 02:17
  • @aaronman, `a` was modified by the pre-incr in that question and that's okay since it only modified it _once._ `a = ++a ...` would have been UB but not the code as given. You can _assume_ assignment was done if you wish but it's neither _in_ the question, nor necessary for the changed `a` or `printf` to make sense. But we're getting off-track here, so I'll shut up now :-) – paxdiablo Aug 02 '13 at 02:22
  • das, I'd suggest fixing up that "expressions evaluate left to right" bit, While that's true for some (including the current case `a || b`), it's not the case for all expressions. C99 6.5/3 states that evaluation of subexpressions (and side effects) is unspecified. – paxdiablo Aug 02 '13 at 03:00
  • 1
    @paxdiablo You are right, the left-to-right statement was too general. I edited to clarify. Thanks! – Sergey Kalinichenko Aug 02 '13 at 03:31
3

There's no sequence point in the expression:

z = ++x || ++y && ++z;

between the pre-increment of z and assignment to z.

So, if the ++z is actually evaluated, that puts you instantly into undefined behaviour territory and anything can happen. You are not permitted to modify the same object twice without an intervening sequence point. Annex C (from C99) lists all the sequence points and the controlling one here is following a full-expression (the entire calculation and assignment).

6.5 Expressions /2 states:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

However, given your initial value of x as 1, the ++z part of the expression is not evaluated, in this particular case. That doesn't make the expression itself any less dangerous since it will invoke UB in the case where the starting point is x == -1 and y != -1.

In this case, the controlling part of the standard is 6.5.14 Logical OR operator /4:

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, the ++x is evaluated first and, because it evaluates to not-zero, ++y && ++z is never evaluated. x is incremented to 2 and z is set to the "truthy" value of that, or 1, y remains untouched at 1.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    @dasblinkenlight, he's modifying `z` twice with no intervening sequence point, the pre-incr and the assignment. Without the assignment, it would be an order-of-evaluation problem. _With_ that, it's UB. – paxdiablo Aug 02 '13 at 02:07
  • 1
    Ah yes - I looked at the right side of `=`, did not see that he's modifying `z` twice!!! – Sergey Kalinichenko Aug 02 '13 at 02:10
  • You are definitely right about UB with the assignment to `z`. However, even with no assignment of `z` the result remains the same ([demo on ideone](http://ideone.com/zR0EOl)). – Sergey Kalinichenko Aug 02 '13 at 02:15
  • When x is `1` in the beginning, the subexpression `++z` is not evaluated, therefore no UB. – Cubbi Aug 02 '13 at 02:21
  • @Cubbi, I believe you misunderstand UB. The compiler doesn't necessarily _know_ the value of `x` at the expression start. It's therefore free to generate whatever code it wants in response to badly formed source, even going so far as to do nothing, or set all variables to 42. UB doesn't become defined simply because the data being fed into an expression changes. The _probabilities_ of certain errant behaviour may be low but _possibilities_ are not limited in any way (as per C99 3.4.3/2). – paxdiablo Aug 02 '13 at 02:27
  • In other words, undefined means **undefined**, not "less defined" :-) – paxdiablo Aug 02 '13 at 02:30
  • @paxdiablo I disagree: it doesn't say that an expression that may potentially break the rules is undefined; it says that `by the evaluation of an expression` (C99). No evaluation, no UB. – Cubbi Aug 02 '13 at 02:35
  • @Cubbi, actually, you appear to be right on that point. It's the evaluation of the subexpression combined with assignment that causes UB. Though the answer is still correct (my "brainfart" was in a comment), I'll adjust it to make that clear. – paxdiablo Aug 02 '13 at 02:48
  • Both the || and && operators force left-to-right evaluation, *and* both introduce sequence points; there is no undefined behavior here. – John Bode Aug 02 '13 at 03:37
  • @JohnBode, the sequence point for &&/|| is after the _first_ expression, there is none between the _second_ expression and the assignment. Hence `z = a && ++z;` will be UB if `a` is nonzero. – paxdiablo Aug 02 '13 at 03:50