For i = 0, why is (i += i++) equal to 0?
Asked Answered
H

24

255

Take the following code (usable as a Console Application):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

The result of i is 0. I expected 2 (as some of my colleagues did). Probably the compiler creates some sort of structure that results in i being zero.

The reason I expected 2 is that, in my line of thought, the right hand statement would be evaluated first, incrementing i with 1. Than it is added to i. Since i is already 1, it is adding 1 to 1. So 1 + 1 = 2. Obviously this is not what's happening.

Can you explain what the compiler does or what happens at runtime? Why is the result zero?

Some-sort-of-disclaimer: I'm absolutely aware you won't (and probably shouldn't) use this code. I know I never will. Nevertheless, I find it is interesting to know why it acts in such a way and what is happening exactly.

Hypophosphite answered 22/11, 2012 at 16:22 Comment(20)
shouldn't the expected result be 1? i(0) += i++(1) therefore 0 += 1 = 1Immigrant
@aleaton you get 0 with the postfix, as += only evaluates i once and the postfix increments last. To get 1, you have to use the prefix.Veradia
It works as expectedPlantar
Preincrementation will increment value before doing the acction i += ++i will give you 1Luu
What the compiler does is right. It is a race condition too!Carlina
How would you pronounce i++ aloud? I advice you to always read it as 'i POSTINCREMENT' inside your head, rather then 'i plus plus'. :)Towers
Why is everybody focussing on pre- vs postincrement? The "strange" thing is that the value of i on the left-hand side of += is "cached" before the right-hand side is evaluated. This is counter-intuitive, as it would, for instance, require a copy operation if i were an object. (Please do not mis-understand me: I absolutely agree to stating that 0 is the correct and standard-conforming answer.)Potomac
@Immigrant I think they expected it to increment first, then add, which would result in 2.Denmark
This doesn't answer your question, but whatever i += i++; was intended to do, I guarantee there's a simpler and clearer way to do it.Averi
I have made an analysis of the problem using functions, that I found to be very clarifying: https://mcmap.net/q/109505/-for-i-0-why-is-i-i-equal-to-0 ... do you agree? Or am I still missing something?Rupertruperta
@Jonny Actually, in C this expression produces 1 (gcc) as many of us have expected and this is what makes the question interesting.Manners
Why, oh why do you people use this disgrace to coding style? Never, never, never ever use post- or preincrement or decrement on same variable in RH- and LH-hand. This expression is confusing because it isn't ment to be used. Ever. Never.Rowe
The very fact that this question has generated so much discussion is proof that the result is anything but expected. As such, for maintenance and readability purposes, the intent of the code is anything but clear and therefore really bad practice. So to anyone who's reading this: Don't use shortcuts like this. Just write clear and simple code so it is clear to everyone exactly what you're trying to accomplish.Weekday
Why the heck does everyone seem to default to using the POST inc/dec operators instead of the PRE operators? The Post operations are less efficient AND can produce surprising side effects - they should never be used unless you're SURE it's what you want. For example, 'for' loops should use '++i', NOT 'i++' - but the majority of programmers seem to use the 2nd one for some reason. I think the wrong one caught on for some reason long ago, and it gets replicated by everyone and used by default.Therine
@Ken Beckett, but that would require you to take into account that your i in the for loop would start at 1 (you could set i = -1 or use i-1 inside your loop logic, but I think most find that looks strange). So we use i++ and I believe the misunderstanding comes from that usage.Hypophosphite
@Peter, no that's wrong - using "++i" or "i++" in the iteration (3rd) expression of a 'for' loop produces identical behavior. Even if you used a 'while' loop with the increment at the bottom, the behavior would be identical. The pre/post nature only applies to the current expression - people seem to have a really big problem with understanding the scope. Why would you think that "for (int i = 0; i < N; ++i)" would increment 'i' before executing the body of the loop or the conditional? It's always worked the same way in C/C++/C#/Java/etc. Using 'i++' in a loop is a bad practice.Therine
@Ken Beckett: The reason is probably because I'm just reading it from left to right. Though, if they behave the same, why is it a bad practice? Apart from causing confusion perhaps (of which my question is an excellent proof)?Hypophosphite
Not only is this code hard to read, but it also has unexpected side effects. Using code like this in production is a very bad idea. I hope no one coming across this gets the idea to use this in their code.Anxious
YOU ARE NOT GOING TO WRITE THAT CODEHounding
possible duplicate of What's the difference between X = X++; vs X++;?Spearmint
C
424

This:

int i = 0;
i += i++

Can be seen as you doing (the following is a gross oversimplification):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

What actually happens is more involved than that - take a look at MSDN, 7.5.9 Postfix increment and decrement operators:

The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:

  • If x is classified as a variable:

    • x is evaluated to produce the variable.
    • The value of x is saved.
    • The selected operator is invoked with the saved value of x as its argument.
    • The value returned by the operator is stored in the location given by the evaluation of x.
    • The saved value of x becomes the result of the operation.

Note that due to order of precedence, the postfix ++ occurs before +=, but the result ends up being unused (as the previous value of i is used).


A more thorough decomposition of i += i++ to the parts it is made of requires one to know that both += and ++ are not atomic (that is, neither one is a single operation), even if they look like they are. The way these are implemented involve temporary variables, copies of i before the operations take place - one for each operation. (I will use the names iAdd and iAssign for the temporary variables used for ++ and += respectively).

So, a closer approximation to what is happening would be:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;
Cimex answered 22/11, 2012 at 16:25 Comment(8)
@Cimex The ++ operation is done before the evaluation of the statement completes. So += overwrites the value. Is this what happened?Unprovided
@Cimex actually its: int i = 0; i = i + 1; (postfix) i = 0; (assignment). If you used i elsewhere in that statement, it would evaluate to 1 at the time.Retrogressive
@Cthulhu - Essentially. The answer by dtb goes into the details.Cimex
Looks like this one should be accepted as the correct answer because it's based on a standard (the same specification is also here: ecma-international.org/publications/standards/Ecma-334.htm ).Off
I don't buy this one. The answer by @yoriy is much more accurate. For one, in your answer, you say that last line would be i+1 whereas it should be i=i+1. Isn't that what i++ is?Sodomite
@Sodomite - You are quite right. I oversimplified for the sake of making the point that ++ and += are not atomic.Cimex
The first part of the answer is redundant. Your last code sample could've done it IMHO. +1 though.Vedanta
That last example should really show "int iAdd = i;" and "int iAssign = i;".Therine
R
196

Disassembly of the running code:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Equivalent code

It compiles to the same code as the following code:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Disassembly of the second code (just to prove they are the same)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Opening disassembly window

Most people don't know, or even don't remember, that they can see the final in-memory assembly code, using Visual Studio Disassembly window. It shows the machine code that is being executed, it is not CIL.

Use this while debuging:

Debug (menu) -> Windows (submenu) -> Disassembly

So what is happening with postfix++?

The postfix++ tells that we'd like to increment the value of the operand after the evaluation... that everybody knows... what confuses a bit is the meaning of "after the evaluation".

So what does "after the evaluation" means:

  • other usages of the operand, on the same line of code must be affected:
    • a = i++ + i the second i is affected by the increment
    • Func(i++, i) the second i is affected
  • other usages on the same line respect short-circuit operator like || and &&:
    • (false && i++ != i) || i == 0 the third i is not affected by i++ because it is not evaluated

So what is the meaning of: i += i++;?

It is the same as i = i + i++;

The order of evaluation is:

  1. Store i + i (that is 0 + 0)
  2. Increment i (i becomes 1)
  3. Assign the value of step 1 to i (i becomes 0)

Not that the increment is being discarded.

What is the meaning of: i = i++ + i;?

This is not the same as the previous example. The 3rd i is affected by the increment.

The order of evaluation is:

  1. Store i (that is 0)
  2. Increment i (i becomes 1)
  3. Store value of step 1 + i (that is 0 + 1)
  4. Assign the value of step 3 to i (i becomes 1)
Rupertruperta answered 22/11, 2012 at 17:33 Comment(16)
+1++ - for sheer hardcore dissection. Chuck Norris would be proud :) I guess you do make the assumption that the OP is on Intel, not a Mono port though ...Natasha
C# has a well-defined evaluation order for the expression, and the object code just implements that order. The machine code output is not the reason or explanation for the evaluation order.Appreciate
The machine code makes it easy to understand how the order of evaluation is implemented IMO.Peristyle
@Natasha I see what you did there. Shame about that discarded upvote though.Bridgid
@Kaz: so what is an explanation for this? A compiler is a man-made work... there is no nature (like in physics or maths) to explain. There is no reason at all for this. They could have implemented it so that the result would be 1 in this case, without affecting all the other ordinary postfix++ operator usages.Rupertruperta
Some inconsistencies: if a = a + b is the same as a = b + a... then why a = a + a++ is not the same as a = a++ + a? The real question here is... what does C# consider as being one instruction?Rupertruperta
The machine code only reveals something about the evaluation scheme, because it is not optimized. Optimized machine code could leave just the initialization of i to zero, leaving you none the wiser. Assuming that i is even used. If not, i could just disappear completely. If i is used (such as to print its value), the compiler could just replace the occurrences of i with an immediate zero operand, seeing that i is not modified anywhere (after i += i++ is optimized away).Appreciate
a++ + a is not the same as a + a++ because this is no longer pure math. The commutativity law in algebra does not take into account the possibility that variables change value midway through an expression. Mathematics only maps neatly to programming when programming is functional programming. And not even then, because of representational limitations. For instance floating-point numbers sometimes behave like reals and sometimes not. Even without side effects, commutativity and associativity laws that apply to real numbers in math break over floating-point numbers.Appreciate
Optimization is meant to make things faster... not to change the meaning of the code... so the analisys of DEBUG code is valid when we are talking about the meaning of code.Rupertruperta
@Kaz: you're right about a++ + a... the postfix++ seems to affect every a that comes after the operation, even inside the same line of code.Rupertruperta
I cover this in my answer. It is simply strict bottom-up, left-right evaluation. The + operator in a++ + a completely evaluates the left hand operand, including all of its side effects, and then the right hand operand.Appreciate
Yeap! C# seems to be work very well in this matter... it respects lazy evaluation opperators and it also works with functions like Func(i++, i++, i++, i++)... the i++ operation takes effect just after each argument is pushed.Rupertruperta
Posted another answer with a more C#ish analysis of the problem: https://mcmap.net/q/109505/-for-i-0-why-is-i-i-equal-to-0Rupertruperta
+1, great answer. Is the eax set to i (0) the second time (where you typed "why?") because of some intermediate language C# is compiled into?Vedanta
@Bane: no... that is just because I didn't realize that the code was not optimized by the compiler. There are two assignments: to tempVar1 and to tempVar2, being set to the same thing... but non-optimized code, doesn't care if they are the same.Rupertruperta
All this confusion and some arguments all point towards the obvious: ++ and -- operators were bad ideas and never should have been included in the language in the first place! They are holdovers from C, and they were a bad idea in C too. I think ruby made the right choice of rejecting them. They are confusing and make code needlessly complex. I'd be okay with increment/decrement statements: standalone i++ or i--. But allowing them as operators was a mistake.Diskin
S
61
int i = 0;
i += i++;

is evaluated as follows:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

i.e. i is changed twice: once by the i++ expression and once by the += statement.

But the operands of the += statement are

  • the value i before the evaluation of i++ (left-hand side of +=) and
  • the value i before the evaluation of i++ (right-hand side of +=).
Sob answered 22/11, 2012 at 16:31 Comment(1)
Ah this is a fantastic explanation. Reminds me of when I worked on a stack based calculator using reverse polish notation.Brindle
C
36

First, i++ returns 0. Then i is incremented by 1. Lastly i is set to the initial value of i which is 0 plus the value i++ returned, which is zero too. 0 + 0 = 0.

Covert answered 22/11, 2012 at 16:26 Comment(2)
But it's i += i++;, not i = i++;, so the value of i++ (0) is added to i, and not "i is set to the value i++ returned". Now the question is, when adding the value i++ returned to i, will i be the incremented value or the not-incremented value? The answer, my friend, is written in the specs.Kickshaw
True, I'll fix it. But anyway since i = 0 initially, i += something is equivalent to i = 0 + something which is i = something.Covert
A
32

This is simply left to right, bottom-up evaluation of the abstract syntax tree. Conceptually, the expression's tree is walked from top down, but the evaluation unfolds as the recursion pops back up the tree from the bottom.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

Evaluation begins by considering the root node +=. That is the major constituent of the expression. The left operand of += must be evaluated to determine the place where we store the variable, and to obtain the prior value which is zero. Next, the right side must be evaluated.

The right side is a post-incrementing ++ operator. It has one operand, i which is evaluated both as a source of a value, and as a place where a value is to be stored. The operator evaluates i, finding 0, and consequently stores a 1 into that location. It returns the prior value, 0, in accordance with its semantics of returning the prior value.

Now control is back to the += operator. It now has all the info to complete its operation. It knows the place where to store the result (the storage location of i) as well as the prior value, and it has the value to added to the prior value, namely 0. So, i ends up with zero.

Like Java, C# has sanitized a very asinine aspect of the C language by fixing the order of evaluation. Left-to-right, bottom-up: the most obvious order that is likely to be expected by coders.

Appreciate answered 22/11, 2012 at 18:19 Comment(6)
+1: I agree with you, except in that every coder expect that... I expected it to be the same as something like this: SetSum(ref i, Inc(ref i)) with int SetSum(ref int a, int b) { return a += b; } and int Inc(ref int a) { return a++; }... of course I no more expect that.Rupertruperta
Also, what I expected is inconsistent! It would not equal to Set(ref i, Sum(i, Inc(ref i))) with int Set(ref int a, int b) { return a = b; } and int Sum(int a, int b) { return a + b; }.Rupertruperta
Thanks; you hint at a flaw/incompleteness in my answer which I have to fix.Appreciate
The issue with SetSum is that it doesn't evaluate the left operand i, but only takes its address, so it's not equivalent to a complete left to right evaluation of the operand. You need something like SetSum(ref i, i, PostInc(ref i)). The second argument of SetSum is the value to be added, where we just use i to specify the prior value of i. SetSum is just int SetSum(ref int dest, int a, int b) { return dest = a + b; }.Appreciate
The confusion happens (at least for me) with the += operator, because assignment operator has right-to-left evaluation (e.g. a = b = c = d)... so one can imagine that += follows the same rule, as an atomic operation (as I did with my SetSum method)... but what happens in fact is that C# translates a += b into a = a + b... showing that += operator is not atomic... it is just syntactic sugar.Rupertruperta
Assignment has right to left associativity, but the evaluation of operands can still be left to right. So if a += b has left to right evaluation, it should lift the value of a and remember it, before moving on to b. In a straight assignment like a = b, we do not need the prior value of a, so that doesn't arise.Appreciate
I
30

Because i++ first returns the value, then increments it. But after i is set to 1, you set it back to 0.

Idealist answered 22/11, 2012 at 16:24 Comment(0)
P
17

The post-increment method looks something like this

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

So basically when you call i++, i is increment but the original value is returned in your case it's 0 being returned.

Platt answered 22/11, 2012 at 16:36 Comment(0)
L
12

The post fix increment operator, ++, gives the variable a value in the expression and then do the increment you assigned returned zero (0) value to i again that overwrites the incremented one (1), so you are getting zero. You can read more about increment operator in ++ Operator (MSDN).

Larine answered 22/11, 2012 at 16:24 Comment(0)
W
12

i++ means: return the value of i THEN increment it.

i += i++ means: Take the current value of i. Add the result of i++.

Now, let's add in i = 0 as a starting condition. i += i++ is now evaluated like this:

  1. What's the current value of i? It is 0. Store it so we can add the result of i++ to it.
  2. Evaluate i++ (evaluates to 0 because that's the current value of i)
  3. Load the stored value and add the result of step 2 to it. (add 0 to 0)

Note: At the end of step 2, the value of i is actually 1. However, in step 3, you discard it by loading the value of i before it was incremented.

As opposed to i++, ++i returns the incremented value.

Therefore, i+= ++i would give you 1.

Weekday answered 22/11, 2012 at 20:52 Comment(1)
It is help FullAdena
C
12

Simple answer

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;
Carlina answered 23/11, 2012 at 5:34 Comment(0)
M
8

i += i++; will equal zero, because it does the ++ afterwards.

i += ++i; will do it before

Mcginn answered 22/11, 2012 at 16:25 Comment(1)
If it does ++ afterwards, I'd expect the result to be 1.Allure
V
8

The ++ postfix evaluates i before incrementing it, and += only evaluates i once.

Therefore, 0 + 0 = 0, as i is evaluated and used before it is incremented, as the postfix format of ++ is used. To get i incremented first, use the prefix form (++i).

(Also, just a note: you should only get 1, as 0 + (0 + 1) = 1)

References: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+=)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)

Veradia answered 22/11, 2012 at 16:26 Comment(0)
R
8

What C# is doing, and the "why" of the confusion

I also expected the value to be 1... but some exploration on that matter did clarify some points.

Cosider the following methods:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

I expected that i += i++ to be the same as SetSum(ref i, Inc(ref i)). The value of i after this statement is 1:

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

But then I came to another conclusion... i += i++ is actually the same as i = i + i++... so I have created another similar example, using these functions:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

After calling this Set(ref i, Sum(i, Inc(ref i))) the value of i is 0:

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

This not only explains what C# is doing... but also why a lot of people got confused with it... including me.

Rupertruperta answered 23/11, 2012 at 19:52 Comment(2)
Please add this into your original answer, it doesn't add any benefit to have it as a separate answer.Ovenbird
I did this not to polute the other answer, since it is about the decompiled code... while in this one, I tried a different approach to explain things. What do you think? Should I edit the other answer and append this one? Maybe, prepend this one... don't know! Thanks for suggestions!Rupertruperta
F
7

A good mnemonic I always remember about this is the following:

If ++ stands after the expression, it returns the value it was before. So the following code

int a = 1;
int b = a++;

is 1, because a was 1 before it got increased by the ++ standing after a. People call this postfix notation. There is also a prefix notation, where things are exactly the opposite: if ++ stands before, the expression returns the value that it is after the operation:

int a = 1;
int b = ++a;

b is two in here.

So for your code, this means

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

i++ returns 0 (as described above), so 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyers describes the difference between those two notations in "Effective C++ programming". Internally, i++ (postfix) remembers the value i was, and calls the prefix-notation (++i) and returns the old value, i. This is why you should allways use ++i in for loops (although I think all modern compilers are translating i++ to ++i in for loops).

Freudian answered 23/11, 2012 at 10:52 Comment(1)
I tested int i = 0; i += (++i), and i is set to one rather than two. It makes sense to me too, since using the prefix instead of the postfix does not change the fact that, if you write i += (++i) out to i = i + (++i), the i is evaluated before ++i, resulting in i = 0 + (++i) and ultimately i = 0 + 1.Dollarfish
E
7

The only answer to your question which is correct is: Because it is undefined.

i+=i++; result in 0 is undefined.

a bug in the language evaluation mechanism if you will.. or even worse! a bug in design.

want a proof? of course you want!

int t=0; int i=0; t+=i++; //t=0; i=1

Now this... is intuitive result! because we first evaluated t assigned it with a value and only after evaluation and assignment we had the post operation happening - rational isn't it?

is it rational that: i=i++ and i=i yield the same result for i?

while t=i++ and t=i have different results for i.

The post operation is something that should happen after the statement evaluation.
Therefore:

int i=0;
i+=i++;

Should be the same if we wrote:

int i=0;
i = i + i ++;

and therefore the same as:

int i=0;
i= i + i;
i ++;

and therefore the same as:

int i=0;
i = i + i;
i = i + 1;

Any result which is not 1 indicate a bug in the complier or a bug in the language design if we go with rational thinking - however MSDN and many other sources tells us "hey - this is undefined!"

Now, before I continue, even this set of examples I gave is not supported or acknowledged by anyone.. However this is what according to intuitive and rational way should have been the result.

The coder should have no knowledge of how the assembly is being written or translated!

If it is written in a manner that will not respect the language definitions - it is a bug!

And to finish I copied this from Wikipedia, Increment and decrement operators :
Since the increment/decrement operator modifies its operand, use of such an operand more than once within the same expression can produce undefined results. For example, in expressions such as x − ++x, it is not clear in what sequence the subtraction and increment operators should be performed. Situations like this are made even worse when optimizations are applied by the compiler, which could result in the order of execution of the operations to be different than what the programmer intended.

And therefore.

The correct answer is that this SHOULD NOT BE USED! (as it is UNDEFINED!)

Yes.. - It has unpredictable results even if C# complier is trying to normalize it somehow.

I did not find any documentation of C# describing the behavior all of you documented as a normal or well defined behavior of the language. What I did find is the exact opposite!

[copied from MSDN documentation for Postfix Increment and Decrement Operators: ++ and --]

When a postfix operator is applied to a function argument, the value of the argument is not guaranteed to be incremented or decremented before it is passed to the function. See section 1.9.17 in the C++ standard for more information.

Notice those words not guaranteed...

Eclat answered 26/3, 2014 at 9:13 Comment(7)
I'm not sure I'm following 100%, but you reference C++ documentation, but my question was about C#. The documentation on that is here.Hypophosphite
I was referring to C# in my answer. From the link you provided: The result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. In either case, x itself has the same value after the operation. clearly shows this is not the case when testing.. because i=++i will provide different result from i=i++. Therefore my answer stands.Eclat
Aha, ok, but it is confusing as you reference C++ documentation. So what you're saying is that the specification hasn't been implemented correctly?Hypophosphite
No. what I'm saying is that it is undefined according to the specification, and using undefined will end up in undefined results.Eclat
Undefined in C++, but C# says it should be the same value after the operation, no? That's not the same as undefined (but I agree you shouldn't use it, see my disclaimer, I was just trying to understand what's going on).Hypophosphite
@Hypophosphite it is also undefined in C#.. C# definition does not include the case you provided - and the test case I shown strictly show the result is different than expected.. regardless the fact we can see what happened to yield that result. no one promise that the result will always remain solid. this is why it is undefined and should not be used. but even if we could promise it can remain solid - as you already mentioned - it shouldn't be used..Eclat
"a bug in the language evaluation mechanism if you will.. or even worse! a bug in design." Undefined behavior is not a bug. It's an intentional part of the standard left there to future-proof implementations against changing hardware and allow for better optimization. Because of UB, the compiler is allowed to assume that, for example, it's impossible for an array access to go out of bounds, or that if a pointer is being dereferenced, then it's impossible for it to be NULL. This allows the compiler to generate more efficient code at the expense of safety, which is left up to the programmer.Horsefly
G
4

The ++ operator after the variable makes it a postfix increment. That means the expression i++ evaluates as the value of i prior to adding 1, which in this case is still the default 0. That means zero is what we are adding, so a result of i == 2 after this line executes is impossible.

However, it could, semantically speaking, still end up being 1; we are incrementing i, after all, it's not unreasonable to expect that to actually happen. Why doesn't it? Because i += i++ is syntax sugar for i = i + i++, and if you check the operator precedence rules for C#, assignment (=) happens dead last. That kinda makes sense if you think about it; if = had a higher precedence than, say +, x = a + 2 would result in x == a, because the assignment x = a would be evaluated prior to the math we're trying to do. So, basic assignment of the right-hand side to the left-hand side happens last, after everything else on the right-hand side is evaluated, including i++.

The result:

  1. i is evaluated as 0 in all RValue usages (left to right).
  2. Increment is evaluated; i is assigned the value of 1
  3. Addition is evaluated; 0 + 0 == 0
  4. Assignment occurs; i is assigned to the evaluated RValue 0

So, the incrementing is overwritten by the assignment, essentially being hidden by the combination of variable evaluation and assignment.

If the incrementing were done using the prefix version, ++i, then the answer would be 1, as the expression ++i will evaluate to the value after the operation. And, that incrementation would stack; i += ++i + ++i would leave i with a value of 3. However, when evaluating the "add and assign", what you're adding to is always the value of i before evaluation of this statement began.

The twist is that the combination of + and = into += changes the operator precedence of the addition; semantically it doesn't matter in the expression given in the question, however it can matter, especially when you use *= or /=. Consider the following:

var i = 5;
i *= 3 + 4;

The value of i after these lines execute is 35 (add 3 to 4, then multiply the result by 5 and assign to i), when if you break out the *= to the apparently-equivalent i = i * 3 + 4, by PEMDAS the multiplication occurs first and the answer would be 19 (multiply 5 by 3, then add 4 and assign to i). That's because the combined operator lowers the precedence of the multiplication to be the action performed just prior to assignment; i *= 3 + 4 is equivalent to i = i * (3 + 4), not i = i * 3 + 4 which would if written that way evaluate as i = (i * 3) + 4.

Getupandgo answered 22/11, 2012 at 16:27 Comment(3)
The ++ does not happen after the += statement, it happens during the execution of the +=statement. That's why the effects of the ++ get override by the +=.Sob
Using ++i actually results in 1, not in 2 (my originally 'expected answer').Hypophosphite
Looks like the assignment += overwrites the modification due to the pre- or post-increment in the expression.Augustin
R
4

Be very careful: read the C FAQ: what you're trying to do (mixing assignement and ++ of the same variable) is not only unspecified, but it is also undefined (meaning that the compiler may do anything when evaluating!, not only giving "reasonnable" results).

Please read, section 3. The whole section is well worth a read! Especially 3.9, which explains the implication of unspecified. Section 3.3 gives you a quick summary of what you can, and cannot do, with "i++" and the like.

Depending on the compilers internals, you may get 0, or 2, or 1, or even anything else! And as it is undefined, it's OK for them to do so.

Romanism answered 23/11, 2012 at 12:39 Comment(3)
oops, c# ... I was thrown off by the "gcc" some went through to disassemble the code.Romanism
I missed it was C# too, but liked the answer anyway.Montemontefiascone
@Iain : thanks, I too believe it was worth keeping the answer available, many persons don't know about this (or about that great faq, from Usenet's best time where most of the people with knowledge on a subjcet were going to the same place to update it)Romanism
I
4

The steps in calculation are:

  1. int i=0 //Initialized to 0
  2. i+=i++ //Equation
  3. i=i+i++ //after simplifying the equation by compiler
  4. i=0+i++ //i value substitution
  5. i=0+0 //i++ is 0 as explained below
  6. i=0 //Final result i=0

Here, initially the value of i is 0. WKT, i++ is nothing but: first use the i value and then increment the i value by 1. So it uses the i value, 0, while calculating i++ and then increments it by 1. So it results in a value of 0.

Impedance answered 23/11, 2012 at 13:25 Comment(0)
T
3

There are two options:

The first option: if the compiler read the statement as follows,

i++;
i+=i;

then the result is 2.

For

else if
i+=0;
i++;

the result is 1.

Tripersonal answered 22/11, 2012 at 16:29 Comment(1)
Neither of which is the actual result.Augustin
D
3

There's lot of excellent reasoning in above answers, I just did a small test and want to share with you

int i = 0;
i+ = i++;

Here result i is showing 0 result. Now consider below cases :

Case 1:

i = i++ + i; //Answer 1

earlier I thought above code resemble this so at first look answer is 1, and really answer of i for this one is 1.

Case 2:

i = i + i++; //Answer 0 this resembles the question code.

here increment operator doesn't come in execution path, unlike previous case where i++ has the chance to execute before addition.

I hope this helps a bit. Thanks

Doityourself answered 9/9, 2014 at 13:25 Comment(0)
A
2

Hoping to answer this from a C programming 101 type of perspective.

Looks to me like it's happening in this order:

  1. i is evaluated as 0, resulting in i = 0 + 0 with the increment operation i++ "queued", but the assignment of 0 to i hasn't happened yet either.
  2. The increment i++ occurs
  3. The assignment i = 0 from above happens, effectively overwriting anything that #2 (the post-increment) would've done.

Now, #2 may never actually happen (probably doesn't?) because the compiler likely realizes it will serve no purpose, but this could be compiler dependent. Either way, other, more knowledgeable answers have shown that the result is correct and conforms to the C# standard, but it's not defined what happens here for C/C++.

How and why is beyond my expertise, but the fact that the previously evaluated right-hand-side assignment happens after the post-increment is probably what's confusing here.

Further, you would not expect the result to be 2 regardless unless you did ++i instead of i++ I believe.

Adaxial answered 27/11, 2012 at 19:13 Comment(2)
The preincrement version produces a result of 2 with C++: ideone.com/8dH8tfAugustin
That makes sense. But the pre-increment is a marginally less complicated situation than the post-increment.Adaxial
L
2

Simply put,

i++, will add 1 to "i" after the "+=" operator has completed.

What you want is ++i, so that it will add 1 to "i" before the "+=" operator is executed.

Loosetongued answered 28/11, 2012 at 17:6 Comment(0)
E
0
i=0

i+=i

i=i+1

i=0;

Then the 1 is added to i.

i+=i++

So before adding 1 to i, i took the value of 0. Only if we add 1 before, i get the value 0.

i+=++i

i=2
Englishman answered 23/11, 2012 at 16:4 Comment(0)
F
-4

The answer is i will be 1.

Let's have a look how:

Initially i=0;.

Then while calculating i +=i++; according to value of we will have something like 0 +=0++;, so according to operator precedence 0+=0 will perform first and the result will be 0.

Then the increment operator will applied as 0++, as 0+1 and the value of i will be 1.

Fluoroscopy answered 23/11, 2012 at 7:33 Comment(2)
This answer is wrong. You will not get 1 because when you do 0 += 0++; assignment is after increment ++ but with the value of i interpreted before of ++ (because is a post operator.Selfinterest
Sorry, but this is incorrect. Read my question and you'll see I say the result is 0. If you run the code, you'll see it is effectively 0.Hypophosphite

© 2022 - 2024 — McMap. All rights reserved.