tags:

views:

649

answers:

5
int x = 10;
x += x--;

In C#/.Net, why does it equal what it equals? (I'm purposely leaving the answer out so you can guess and see if you're right)

+45  A: 

Look at this statement:

x += x--;

This is equivalent to:

x = x + x--;

Which is equivalent to:

int a1 = x; // a1 = 10, x = 10
int a2 = x--; // a2 = 10, x = 9
x = a1 + a2; // x = 20

So x is 20 afterwards - and that's guaranteed by the spec.

What's also pretty much guaranteed, although not by the spec, is that anyone using such code will be attacked by their colleagues. Yes, it's good that the result is predictable. No, it's not good to use that kind of code.

Jon Skeet
No fair...Skeet shouldn't be allowed to answer these questions. :P
CAbbott
haha, no skeet should not be allowed!!! Greedy! haha.
Eric
@CAbbott - unfortunately no one else appears to understand the difference between x-- and --x!
Daniel Earwicker
I want to know if Jon did this from memory.
Tom Ritter
Sure, why not, but we all will loose the thrill and energy we put into search for the answer to come up earlier than Jon Skeet. And that is the all fun part.!!
Asad Butt
What if he used --x instead? Is that also guaranteed by the spec?
Justin Ethier
@Tom: Yes, I did. I checked it afterwards, admittedly. I did have to check that you'd specified `x` as `int` though... the first "equivalent to..." would be very slightly different if `x` were a `byte` even though the result would be the same in this case.
Jon Skeet
@Justin: Yes, that would be guaranteed to produce 19 - the value of a2 in the expansion would have been 9. You can get to 18 if you change it to `x = --x + x;`.
Jon Skeet
+1 for "...anyone using such code will be attacked by their colleagues..."
JMarsch
+9  A: 

From the specs 7.13.2

If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

So your statement is equivalent to x = x + x--; which is evaluated in left-to-right order and gives the answer 20.

Note that there is also a difference between --x and x-- here. If you had written x += --x; this would be equivalent to x = x + --x; then you would get 19. This is because the value of x is decremented and the resulting value is used in the expression (unlike x-- where the original value of x is used in the expression).

This expression x = x + --x + x will give 28 because the third timefourth time (see comments) x is evaluated it is 9.

Mark Byers
Maybe you meant 20? 10 would be really surprising..;)
Francesco
@Francesco: Typo, fixed. Now I added one more paragraph and *that* 10 is actually meant to be a 10.
Mark Byers
+1 for quoting the spec.
Jon Skeet
Though the answer you came up with in the last paragraph is correct, your explanation doesn't quite make sense. The decrement cannot be done *before* x is evaluated because obviously the value computed by decrement depends on the evaluation of x! Rather, the right way to say this is that the value of the expression --x is the value that was assigned to x *after* the decrement. That is, the sequence of events is: evaluate x (10), evaluate x again (10), subtract one (9), assign 9 to x, add 10 and 9 (19), assign 19 to x. There are two evaluations of x and they both happen up front.
Eric Lippert
@Eric: Yes, you're right - the word "evaluated" was the wrong word to use there. Thanks for the correction. I've rephrased in my own words. Hopefully it's OK now.
Mark Byers
Better, but still not perfect. You mean the *fourth* time x is evaluated in "x=x+--x+x" it is 9. First x is evaluated on the left hand side of the = to determine the location of the storage; this has no side effects and its value is not used, but it is an evaluation. Then it is evaluated on the right hand side of the = as 10. Then it is evaluated again as 10, 10-1 is computed, and 9 is assigned to x, and 9 is the result of the operation. Then, the *fourth* time x is evaluated, now it is 9.
Eric Lippert
For example, suppose instead we had T I<T>(T t){Console.WriteLine(t); return t;} and class Q { public int x; } and you said I(q).x = I(q).x + --I(q).x + I(q).x; -- you'd expect four lines to be written. The computation of the location of the storage can induce a side effect. Note that when you say I(q).x += 123, the side effect only happens once; we do not actually expand this to I(q).x = I(q).x + 123;
Eric Lippert
@Eric: Yes, that point about the number of evaluations is actually mentioned in the quote from the specs at the start of my answer. And thanks for your patience in correcting these errors in posts here. I'll try to get this right next time.
Mark Byers
+13  A: 

20; the "--" doesn't happen until after everything gets evaluated, and that value is overwritten by the left-hand side of the equals.

bryanjonker
Your name isn't Jon Skeet, but I'll upvote anyway.
Patrick Karcher
Though you've got the right answer, the logic that got you there is incorrect. The "doesn't happen until after everything gets evaluated" is misleading. In particular, the decrement happens before the assignment of 9 to x, which happens before the addition of 10 and 10, which happens before the assignment of 20 to x. The calculation of the decrement happens before one addition and two assignments, so it cannot be correct to say that it doesn't happen until *everything* gets evaluated.
Eric Lippert
A: 

The answer is 20. And Tom, you really aren't as surprised as your questions seems to imply, right? And to those of you who are assuming the answer is 19 - I think you're confused with x += --x;

Eric
What they're confused about is probably not --x, but rather, whether the side effect of "decrement x" happens before or after the computation of the left-hand side of the += operator. In language where x+=x-- is evaluated right-to-left, you first compute x--, which is 10, and assign 9 to x. Then you compute the left hand side, which is now 9. And now you add 9 to 10 and assign that to x, so, 19. C# guarantees that expressions are evaluated left-to-right. C++ does not; a C++ compiler can evaluate this right-to-left if it chooses, and both 19 and 20 are legal results.
Eric Lippert
Eric. I believe you are right. I couldn't agree more.
Eric
+11  A: 

Jon is of course right.

A good way to think about this is to remember:

1) subexpressions are always evaluated left to right. Period. Evaluation of a subexpression may induce a side effect.

2) execution of operators is always done in the order indicated by parentheses, precedence and associativity. Execution of operators may induce a side effect.

The "x" to the left of the += is the leftmost subexpression, and therefore rule (1) applies. Its value is computed first -- 10.

The x-- to the right of the += is the next one in left-to-right order, so it is evaluated next. The value of x-- is 10, and the side effect is that x becomes 9. This is as it should be, because -- is of higher precedence than +=, so its side effect runs first.

Finally, the side effect of += runs last. The two operands were 10 and 10, so the result is to assign 20 to x.

I get questions about this all the time. Remember, the rules are very straightforward: subexpressions left-to-right, operators in precedence order, period.

In particular, note that the commonly-stated reasoning "the -- operator is postfix and therefore runs after everything else" is incorrect reasoning. I discuss why that is incorrect in the articles below.

Here are some articles I've written on this subject:

http://blogs.msdn.com/ericlippert/archive/tags/precedence/default.aspx

Eric Lippert
Addition is a sequence point in C#? You're allowing people to write code without understanding sequence points. Surely the end of the world is at hand.
Ben Voigt