3

Could someone explain me why this short line of code is returning 1?

int i = 0;
if(i++) i++;
printf("%d", i);

I mean when checking the if statement i has to be incremented otherwise the result would not be 1. But then as it is incremented is should be incremented once again resulting in 2.

And even better, why this line of code is resulting 2?

int i = 0;
if(++i || i++) i++;

Also this

int i = 0;
if(++i && i++) i++;

Returns 3.

Lisek
  • 753
  • 2
  • 11
  • 31
  • C11 draft standard n1570: *6.5.2.4 Postfix increment and decrement operators 2 The result of the postfix ++ operator is the value of the operand.[...] The value computation of the result is sequenced before the side effect of updating the stored value of the operand.* – EOF Aug 30 '16 at 21:23
  • 6
    The _result_ of `i++` is 0, so the `if()` is not taken. – chux - Reinstate Monica Aug 30 '16 at 21:24
  • 2
    the postfix form increments the value stored in a variable after the expression is evaluated, thus you have 0 as a test expression in if() – Serge Aug 30 '16 at 21:24
  • Post increment means it increments after the condition is checked. Put something like cout << "blah" in the condition block to help you understand how this works – shortwavedave Aug 30 '16 at 21:25
  • inside the if statement, the value of `i` is checked _before_ it's incremented, that's what postfixed ++ means. Is the value is zero still, then goes to 1 then the statement after the if is not executed as the if condition was zero=false. – Cecil Ward Aug 30 '16 at 21:26
  • you were talking about prefixed ++, in fact – Cecil Ward Aug 30 '16 at 21:27

1 Answers1

2
  • The line of code if(i++) checks the value of i before incrementing. So the check fails since i=0 and after the check, i will be incremented before leaving the if condition making it equal to 1.
  • But if(++i || i++) passes since ++i is evaluated before the condition is checked which evaluates to 1 and the condition will be true and then i++ which will result to 2.
  • Also, since ++i will be 1 and i++ is also still 1 since i will be used first before its incremented. So 1 && 1 is true and then before leaving the condition, i will be incremented to 2 and also the line below the if will be evaluated to 3 and the new value of i will be 3.
Derick Alangi
  • 1,080
  • 1
  • 11
  • 31
  • But then again when I have `if(++i && i++) i++;` the result is 3. – Lisek Aug 30 '16 at 21:27
  • Yes that is correct since `++i` will be 1 and `i++` is also still 1 since `i` will be used first before incrementation. So 1 && 1 is true and then before leaving the condition, `i` will be incremented to 2 and also the line below the `if` will be evaluated to 3 and the new value of `i` will be 3. Its very correct. – Derick Alangi Aug 30 '16 at 21:30
  • @Lisel that is because both expressions are evaluated - the first expression is `true`, so the second must be evalauted due to the `&&` - both expressions must be `true`. – Weather Vane Aug 30 '16 at 21:30
  • The expression is not short-circuited in case of `&&`, as the left side is evaluated to `true`. – Eugene Sh. Aug 30 '16 at 21:31
  • take a look at the generated code, very educational. – Cecil Ward Aug 30 '16 at 21:31
  • 1
    @Liseł: For more fun, what will `if(++i && --i) i++;` result in? – EOF Aug 30 '16 at 21:32
  • I thought that the compiler evaluates both of the expressions in the if statement no matter what if `||` (OR) is used. Well I could not be more wrong I guess. – Lisek Aug 30 '16 at 21:32
  • Short-circuited operations are fun. – Eugene Sh. Aug 30 '16 at 21:32
  • @LIsel not if the truth can be established from the first evaluation: short-circuit. In the case of `&&` if the first expression is false the second is not evaluated. In the case of `||` if the first expression is true the second is not evaluated. – Weather Vane Aug 30 '16 at 21:33
  • in actual practice, if it happens not to violate the standard, then a good compiler may choose to evaluate both parts, if the code generated is faster. But it will have to check for legality in a particular case. Take a look at code generated by GCC with -O3 option. – Cecil Ward Aug 30 '16 at 21:34
  • @CecilWard "if it happens not to violate the standard" or for more practical purpose "if it happens not to change the program semantics". – Eugene Sh. Aug 30 '16 at 21:35
  • this is because good compilers desperately want to avoid branches for speed when the balance of cost is in favour. – Cecil Ward Aug 30 '16 at 21:36
  • @eugene sh quite so – Cecil Ward Aug 30 '16 at 21:36
  • 1
    @CecilWard you can rely on the second part not being evaulated, when it involves the firrst part. For example `if(n >= 0 && array[n] == 42)`. The second part *cannot* be evaluated unless the first part is `true`. – Weather Vane Aug 30 '16 at 21:37
  • @WeatherVane It can if the second part has no side-effects. I remember discussing it somewhere with chux... – Eugene Sh. Aug 30 '16 at 21:38
  • @EugeneSh. if in my example `n = -1` the second will not, and cannot be safely evaluated. – Weather Vane Aug 30 '16 at 21:39
  • I should have said that the condition is quite tight, it has to just produce code that ‘does the right thing’ as if it had used shortcut, which is quite a challenge, but I've often seen code that does what I said, using a `cmov` instruction. Compilers are v intelligent nowadays – Cecil Ward Aug 30 '16 at 21:40
  • 9
    @EOF: "*For more fun, what will `if(++i && --i) i++;` result in?*" -- A failed code review. – Keith Thompson Aug 30 '16 at 21:44
  • 4
    please kids, don't write code like this though, don't require other maintainers to look up the fine details of the standard too often. You won't generated better or worse code by cramming lots of stuff into single complex if statements or putting side effects into tests. – Cecil Ward Aug 30 '16 at 21:45
  • good compilers are so intelligent nowadays that it doesn't usually matter if you change the statement layout to make it a bit simpler for non-experts to read and work out exactly what you _intended_. – Cecil Ward Aug 30 '16 at 21:47
  • @KeithThompson: Are you sure `i += !!i;` will fare better? – EOF Aug 30 '16 at 21:48
  • @EOF Actually looks like a useful construct... Not sure for what, but feels useful :) – Eugene Sh. Aug 30 '16 at 21:54
  • @EOF Ah, OK, it is pretty useful with a modification: `i -= !!i;`. Good for decrementing down to zero. – Eugene Sh. Aug 30 '16 at 21:57
  • "checks the value of i before incrementing." - not necessarily, it could increment and then check the value and then subtract 1 for purposes of the test. It's better to say that the old value is used for the test, and the test may happen at some stage before or after the stored value is incremented. The way you put it, leads to people wondering why `printf("%d %d\n", i++, i++);` can output `1 1` or any other nonsense – M.M Aug 30 '16 at 22:00
  • @M.M That's undefined for multiple unsequenced modifications to a scalar object. It can output "elephant". – EOF Aug 30 '16 at 22:03
  • @M M -agreed, bad wording. It should always have an 'effectively' or an ’as if’ qualifier. Modern compilers are so intelligent that they can radically rearrange and rewrite your code completely as long as it preserves the meaning, _it's as if_ the program generated were the one you actually wrote. – Cecil Ward Aug 30 '16 at 22:14
  • It's very instructive to take a look at generated code with high levels of optimisation set (eg -O2 and -O3 and -Os in GCC) as you might be surprised at how much transformation goes on, including deleting huge chunks of your code altogether on occasion. But advanced compilers don't seem to get things wrong in my experience. – Cecil Ward Aug 30 '16 at 22:18