Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#

Why Does x = ++x + x++ Give Me the Wrong Answer?

Rate me:
Please Sign up or sign in to vote.
4.79/5 (63 votes)
11 Nov 2021CPOL4 min read 94.1K   20   63
When they first meet them, developers are often tempted to play with pre- and post- increment operators. Then they get confused, because they don't do what is expected. Or ... do they?

Introduction

Pre- and post- fix increment and decrement operations are pretty easy in theory, it's only when people get creative that you get problems in practice. Basically, a prefix (++i or --i) says to increase or decrease the value before you use the variable, so the variable has the new value immediately:

C#
i = 10;
x = ++i + 5;

Can be read as:

C#
i = 10;
i = i + 1;
x = i + 5;

and similarly for the -- version:

C#
i = 10;
x = --i + 5;

Can be read as:

C#
i = 10;
i = i - 1;
x = i + 5;

The postfix version (i++ or i--) does the same thing, but after the variable has been used:

C#
i = 10;
x = i++ + 5;

Can be read as:

C#
i = 10;
x = i + 5;
i = i + 1;

And similarly:

C#
i = 10;
x = i-- + 5;

Can be read as:

C#
i = 10;
x = i + 5;
i = i - 1;

Is that it? It's a bit...simple... Yes, it is. Or, perhaps not. It is simple, if you use it in simple ways - as an array indexer for example:

C#
x = myArray[index++];

Or as a loop increment:

C#
for (i = 0; i < 10; i++)
   {
   WriteLine(myArray[i]);
   }

But after that, you are into a world of confusion and pain! For example, what does this leave as a value of i:

C#
int i,j;

i = 10;
for (j = 0; j < 5; j++)
    {
    i = i++;
    }

The answer is: unchanged. i remains at 10. Why? Think of it like this: what does this look like if we expand it?

C#
int i = 10;
i = i++;

If we write this in C#, then the IL looks like this:

MSIL
.line 14,14 : 13,24 ''
IL_0001:  ldc.i4.s   10            Push a constant value '10' to the stack, 4 byte integer, 
IL_0003:  stloc.0                  Pop the top of the stack into local number 0
.line 15,15 : 13,21 ''
IL_0004:  ldloc.0                  Push local number 0 to the stack 
IL_0005:  dup                      Duplicate the top of the stack
IL_0006:  ldc.i4.1                 Push a constant value '1', 4 byte integer
IL_0007:  add                      Pop the top two stack items, add them, and push the result
IL_0008:  stloc.0                  Store the top of the stack in local number 0
IL_0009:  stloc.0                  Store the top of the stack in local number 0

What? In expanded C# code, that comes back as:

C#
int i = 10;
int j = i;
int k = j;
k = k + 1;
i = k;
i = j;

Which is rather strange... because you could throw away the three lines in the middle without affecting the results. It shouldn't do that, should it? Yes. Yes, it should: i++ is a postfix operation: It says, "remember the value of i, then increment i by one, and then return the value you remembered". So what you have told the compiler to do is ignore the result of the increment by overwriting it with the value you started off with. Interestingly, if you try it in the Visual Studio C++ compiler...it doesn't... because it handles it differently! So, now we have the first reason why you have to be careful when you start using increment and decrement operators for real: it's effectively a side effect, a whole line of code inserted into your line, and if you don't think very carefully, it won't do what you think.

C#
int i1 = i; i1 = i1 + 1; i = i

The trouble comes when you start mixing operations on the same line:

C#
i = 10; x = ++i + i++;

The problem is that the language specification does not define exactly when pre- and post- fix operations should occur, or even which order the operators are evaluated, given the operator precedence rules.

Precedence and Order of Evaluation[^] says:

MSDN wrote:
Only the sequential-evaluation (,), logical-AND (&&), logical-OR (||), conditional-expression (? :), and function-call operators constitute sequence points and therefore guarantee a particular order of evaluation for their operands.

So the compiler writer is at liberty to evaluate this expession:

C#
a = b * c + d * e; 

by working out b * c first, then d * e, or d * e first then b * c because it has no effect on the result of the calculation. Unless you start playing with pre and post increment operators of course - then the results change.

Which means that it's implementation specific exactly what you get as a result: The value of i should always be the same: 12 but the value of x can be different depending on the compiler (and to an extent on the target processor - ARM for example has built in pre- and post- fix increment and decrement to its "machine code" LOAD operations, so it would be quite likely that an efficient compiler would use them directly) should it be executed as:

C#
i = 10; 
i = i + 1; 
x = i + i; 
i = i + 1; 

Which gives the result 22 or as:

C#
i = 10; 
i1 = i; 
i = i + 1; 
x = i1 + i; 
i = i + 1;

Which gives 21 Or as:

C#
i = 10; 
i1 = i; 
i = i + 1; 
x = i + i1; 
i = i + 1;

which also gives 21 by a different route. And bear in mind that the compiler does not have to evaluate the two operands of "+" in left to right order, so it could even give some very strange and unexpected results! Like 23...

Worse: the evaluation order could change between compiler versions (because it's not defined) or even as a result of optimizations: so what works in debug fails in release!

So avoid combining them: use them for "simple expressions" such as incrementing an array index each time round a loop, but don't get fancy, or your code may well fail in interesting ways...

Points of Interest

This was written originally as the answer to a question - but I could never find it when I needed it, and felt it needed to be preserved for posterity. Hence it was updated a bit, expanded, published as an article and gets higher visibility.

History

  • 1st September, 2016: Original version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
Wales Wales
Born at an early age, he grew older. At the same time, his hair grew longer, and was tied up behind his head.
Has problems spelling the word "the".
Invented the portable cat-flap.
Currently, has not died yet. Or has he?

Comments and Discussions

 
BugThis article is just plain wrong Pin
Lasse Vågsæther Karlsen15-Nov-21 0:48
Lasse Vågsæther Karlsen15-Nov-21 0:48 
GeneralMy vote of 3 Pin
tbayart14-Nov-21 23:16
professionaltbayart14-Nov-21 23:16 
QuestionThe 'i = i++' example Pin
John Ramsden13-Nov-21 21:01
John Ramsden13-Nov-21 21:01 
AnswerRe: The 'i = i++' example Pin
mike@codeproject16-Nov-21 7:43
mike@codeproject16-Nov-21 7:43 
QuestionI guess C# increments differently than C/C++ (wrong answer?) Pin
greg393873513-Nov-21 5:51
greg393873513-Nov-21 5:51 
AnswerRe: I guess C# increments differently than C/C++ (wrong answer?) Pin
mike@codeproject16-Nov-21 8:09
mike@codeproject16-Nov-21 8:09 
QuestionThanks for the article! Pin
Bill Castle13-Nov-21 3:02
Bill Castle13-Nov-21 3:02 
QuestionIsn't that all very academic Pin
r_alf12-Nov-21 22:51
r_alf12-Nov-21 22:51 
QuestionThe fundamental reason Pin
YDaoust12-Nov-21 21:42
YDaoust12-Nov-21 21:42 
GeneralMachine code Pin
Ray Random12-Nov-21 16:53
Ray Random12-Nov-21 16:53 
GeneralC++ PinPopular
ShawnVN12-Nov-21 15:41
ShawnVN12-Nov-21 15:41 
QuestionDoing it wrongly Pin
Richard Meadows12-Nov-21 14:40
Richard Meadows12-Nov-21 14:40 
AnswerRe: Doing it wrongly Pin
David On Life12-Nov-21 17:35
David On Life12-Nov-21 17:35 
QuestionWhy Does x = ++x + x++ Give Me the Wrong Answer? Pin
Bob Beechey12-Nov-21 13:10
Bob Beechey12-Nov-21 13:10 
AnswerRe: Why Does x = ++x + x++ Give Me the Wrong Answer? Pin
ExcellentOrg12-Nov-21 20:40
ExcellentOrg12-Nov-21 20:40 
x = ++x + x++; is

1) completely absurd code
2) rotten way to write good code
as well as
3) lame way to write obfuscated code.

Net effect of expression will be ++x; Everything else is superfluous and just grunt work
to waste CPU cycles.
GeneralRe: Why Does x = ++x + x++ Give Me the Wrong Answer? Pin
johnybe7512-Nov-21 22:21
johnybe7512-Nov-21 22:21 
QuestionThis should have a subtitle - don't get cute PinPopular
charlieg11-Nov-21 5:51
charlieg11-Nov-21 5:51 
AnswerRe: This should have a subtitle - don't get cute Pin
OriginalGriff11-Nov-21 9:50
mveOriginalGriff11-Nov-21 9:50 
SuggestionMicrosoft replies: No this is about C not about C#. The article is wrong. Pin
alexpeter29-Oct-19 0:16
alexpeter29-Oct-19 0:16 
GeneralMy vote of 3 Pin
AndreyPetrov@Ukraine10-Aug-18 10:37
AndreyPetrov@Ukraine10-Aug-18 10:37 
GeneralRe: My vote of 3 Pin
charlieg11-Nov-21 5:47
charlieg11-Nov-21 5:47 
AnswerRe: Why does x = ++x + x++ give me the wrong answer? Pin
KritskiYuri20-Nov-17 20:11
professionalKritskiYuri20-Nov-17 20:11 
QuestionInterestingly, ++x+x++ should give the same result regardless of evaluation order Pin
hpcoder220-Nov-17 12:20
hpcoder220-Nov-17 12:20 
AnswerRe: Interestingly, ++x+x++ should give the same result regardless of evaluation order Pin
David On Life12-Nov-21 17:47
David On Life12-Nov-21 17:47 
QuestionNo confusion if you are a half decent programer - you would never use somethign like that! PinPopular
Bob100020-Nov-17 9:44
professionalBob100020-Nov-17 9:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.