27

The following two C# code snippets produce different results (assuming the variable level is used both before and after the recursive call). Why?

public DoStuff(int level)
{
  // ...
  DoStuff(level++);
  // ...
}

,

public DoStuff(int level)
{
  // ...
  DoStuff(level+1);
  // ...
}

After reading some of the responses below I thought it would be worthwhile posting the stack traces for level++, ++level and level+1 to highlight how deceiving this problem is.

I've simplified them for this post. The recursive call sequence starts with DoStuff(1).

// level++

DoStuff(int level = 1)
DoStuff(int level = 2)
DoStuff(int level = 2)
DoStuff(int level = 2)

// ++level

DoStuff(int level = 4)
DoStuff(int level = 4)
DoStuff(int level = 3)
DoStuff(int level = 2)

// level+1

DoStuff(int level = 4)
DoStuff(int level = 3)
DoStuff(int level = 2)
DoStuff(int level = 1)
Jacob Schoen
  • 14,034
  • 15
  • 82
  • 102
Richard Dorman
  • 23,170
  • 16
  • 45
  • 49
  • Great question and great answer! I've been using C++ for years and C# more recently, and I had no idea! – Joshua Carmody Sep 22 '08 at 14:38
  • Your stack traces are wrong. level++ should be 1, 1, 1, 1; ++level should be 1, 2, 3, 4; and level + 1 should be 1, 2, 3, 4 – Orion Adrian Sep 23 '08 at 12:16
  • Orion - the stack traces were taken directly from VS2008. I traced the function calls to four levels of recursion and did a cut and paste. – Richard Dorman Sep 23 '08 at 20:37
  • Ah.. I'm writing the stack traces with the last call being at the bottom, rather than at the top. I'm not looking at the values of level, but rather what value is being passed to each function. To properly evaluate the stack, you should put a breakpoint at the first line of the function. – Orion Adrian Sep 26 '08 at 16:42

15 Answers15

44

To clarify all the other responses:

+++++++++++++++++++++

DoStuff(a++);

Is equivalent to:

DoStuff(a);
a = a + 1;

+++++++++++++++++++++

DoStuff(++a);

Is equivalent to:

a = a + 1;
DoStuff(a);

+++++++++++++++++++++

DoStuff(a + 1);

Is equivalent to:

b = a + 1;
DoStuff(b);

+++++++++++++++++++++

Matt
  • 935
  • 6
  • 16
  • 1
    Except your example for `DoStuff(a++)` is wrong. It should be: int temp = a; a = a + 1; DoStuff(temp); – Orion Adrian Sep 22 '08 at 12:18
  • Parameters are never evaluated after the function call to which they belong. Compiler optimizations may change the order of calls, but that goes beyond this simple example. Any number of things may be re-organized. – Orion Adrian Sep 22 '08 at 15:18
  • a++ creates a temporary variable before the call with the old value and increments straight away, not an increment afterwards. In certain situations, the difference is very pronounced. – workmad3 Oct 16 '08 at 13:56
  • is the first example really right? in C++, at least your example is wrong. evaluations of arguments are complete before the call is made (there is a sequence point just before the call is made). If that is true for C# too, then your example (the first one) is wrong. – Johannes Schaub - litb Dec 29 '08 at 02:00
  • If a for example is a member variable and DoStuff accesses it, it will find the old value using your example, while (in C++ at least) it will find the new value (although the parameter, of course, contains the old value) in real code. – Johannes Schaub - litb Dec 29 '08 at 02:03
  • This is probably an example of where common knowledge is wrong because it seems despite the very pronounced wrongness of this answer, it's still by and far the most up-voted answer. Sometimes common knowledge isn't the truth. – Orion Adrian Jun 29 '09 at 14:53
  • @Orion Adrian: At least in plain old C (according to K&R), a++ increments a and yields the value before the increment, and ++a increments the value and yields the new value with the increment. I expect at least C++ to work the same way, but I'm not sure. So the example should be correct (if C# works the same way). – Kimmo Puputti Mar 16 '10 at 10:12
  • @Kimmo: The problem is in a++. DoStuff(a++) will do the increment immediately, but the result of the a++ function is the unincremented value. The above applies that DoStuff(a++) will not increment the a variable until after DoStuff is executed. See Frederik's or my answer for a more precise translation. – Orion Adrian Mar 19 '10 at 18:52
  • 1
    The point of ensuring that f(x++) is not equivalent to f(x); x = x + 1; is when you get to something like f(x++, x++) you're not going to think that you'll end up with f(x, x); x = x + 1; x = x + 1; Instead you're going to get temp = x; x = x + 1; temp2 = x; x = x + 1; f(temp, temp2);. That's why the following is a bad presumption. It also leads to "magical" thinking in that paramters can be modified after the call returns. – Orion Adrian Mar 10 '11 at 15:42
29

Because the first example is really equivalent to:

public DoStuff(int level)
{  
  // ...
  int temp = level;
  level = level + 1;
  DoStuff(temp);
  // ...
}

Note that you can also write ++level; that would be equivalent to:

public DoStuff(int level)
{  
  // ...
  level = level + 1;
  DoStuff(level);
  // ...
}

It's best not to overuse the ++ and -- operators in my opinion; it quickly gets confusing and/or undefined what's really happening, and modern C++ compilers don't generate more efficient code with these operators anyway.

Frederik Slijkerman
  • 6,471
  • 28
  • 39
  • Agreed as to not overusing them. What is also 'great fun' is overloading post and pre ++ with a class as then all bets are off. – workmad3 Sep 22 '08 at 12:03
  • 1
    I have to disagree; `++` and `--` are extraordinarily intuitive and easy. The only time problems arise is when people either try to get cute or don't even bother to look up the behavior of the operators they're using. – Hank Gay Sep 22 '08 at 12:03
  • So what's intuitive and easy about this? :-) DoMoreStuff(++level, ++level); – Frederik Slijkerman Sep 22 '08 at 12:08
  • 3
    I think that's classed as 'trying to get cute' :) – workmad3 Sep 22 '08 at 12:14
  • Additionally, his example is wrong. I'm not amazed how often people don't quite understand post-increment (variable++), but I see it wrong almost every time. Post-increment does not evaluate after the call. It evaluates before the call, specifically: int temp = a; a = a + 1; DoStuff(temp); – Orion Adrian Sep 22 '08 at 12:22
  • That's a fairly subtle distinction for a local variable, though – Simon Buchan Sep 22 '08 at 12:40
  • The 2 are equivalent for the current example (apart from new temporary creation of course). More complicated examples would change this though. – workmad3 Sep 22 '08 at 12:46
  • I have to agree with Orion. This answer demonstrates the point slightly but is missleading in terms of what actually happens. – Henry B Sep 22 '08 at 13:47
  • It's one of those cases where it tends to teach people the wrong order of things (which goes wonky when people try to generalize). It also leads to the idea that the use of i++ increments after the for loop while ++i increments before the for loop; and other such craziness. – Orion Adrian Sep 22 '08 at 13:51
  • Your second code snippet isn't equivalent to the original question's though. In the original question the level variable is never modified. If it's used elsewhere (we don't know) then your way would produce different results. – Herms Sep 22 '08 at 14:27
  • OK, I've edited the example to be more correct. I do believe that this discussion shows exactly why you shouldn't overuse the pre/postincrement operators: there are subtle side effects that aren't obvious. – Frederik Slijkerman Sep 22 '08 at 14:37
  • @Herms: It isn't supposed to be equivalent to the original question. – Frederik Slijkerman Sep 22 '08 at 14:38
  • Even post editing, however, your example is still incorrect. There should be no code after the call to DoStuff, as all parameters are evaluated before the actual call to the function. To imply otherwise is incorrect. – Orion Adrian Sep 22 '08 at 15:13
27

level++ will pass level into DoStuff and then increment level for use in the rest of the function. This could be a fairly nasty bug as the recursion will never end (from what is shown DoStuff is always being passed the same value). Perhaps ++level is meant instead, as this is the opposite of level++ (increments level and passes the incremented value into DoStuff)?

level+1 will pass level+1 into DoStuff and leave level unchanged for the rest of the function.

workmad3
  • 25,101
  • 4
  • 35
  • 56
12

the return value of level++ will be level and therefore pass level into DoStuff. This could be a fairly nasty bug as the recursion will never end (from what is shown DoStuff is always being passed with the same value). Perhaps ++level or level + 1 is meant instead?

level + 1 will pass level + 1 into DoStuff and leave level unchanged for the rest of the function.


The post-increment operator (variable++) is precisely equivalent to the function

int post_increment(ref int value)
{
    int temp = value;
    value = value + 1
    return temp;
}

while the pre-increment operator (++variable) is precisely equivalent to the function

int pre_increment(ref int value)
{
    value = value + 1;
    return value;
}

Therefore, if you expand the operator inline into the code, the operators are equivalent to:

DoStuff(a + 1)

int temp = a + 1;
DoStuff(temp);

DoStuff(++a)

a = a + 1;
DoStuff(a);

DoStuff(a++);

int temp = a;
a = a + 1;
DoStuff(temp);

It is important to note that post-increment is not equivalent to:

DoStuff(a);
a = a + 1;

Additionally, as a point of style, one shouldn't increment a value unless the intention is to use the incremented value (a specific version of the rule, "don't assign a value to a variable unless you plan on using that value"). If the value i + 1 is never used again, then the preferred usage should be DoStuff(i + 1) and not DoStuff(++i).

Orion Adrian
  • 19,053
  • 13
  • 51
  • 67
  • What you say is 100% true. But it is worth mentioning that for the post-increment version, the compiler is allowed to omit the temporary and relocate the inc till after usage for simple situations (such as using basic types). – Evan Teran Sep 22 '08 at 13:12
  • Evan that's a kind of optimization that the compiler MIGHT make, but it also the kind of optimization that could cause very subtle problems. – Henry B Sep 22 '08 at 13:48
  • It's also not an optimization that you can rely on. It's an implementation detail of the compiler and therefore isn't something that you should say definitely happens unless you're also willing to say that it happens in these versions of these compilers. – Orion Adrian Sep 22 '08 at 13:53
  • There's a mistake in your first code sample. temp is declared but never used. – Matt Howells Sep 22 '08 at 23:55
  • Orion - Your level++ stack trace is incorrect. The first call to DoStuff places a value of 1 onto the stack. This is then modified to 2 before the second call occurs but after the value is placed on the stack for the next call.Your stack ends up being a series of 2s. – Richard Dorman Sep 23 '08 at 21:00
  • Orion - Your ++level stack is also incorrect. Similiar reasoning to my previous comment except the result is 5,4,3,2 (or 4,4,3,2 depending on where in the call you take the stack snap shot). This can be confirmed in VS2005 or VS2008. – Richard Dorman Sep 23 '08 at 21:00
  • I'm not exactly following you here. If you look at the level++ version, if the first call is DoStuff(1) then the next call (first recursive call) will be DoStuff(1) because the return value of 1++ is 1, not 2. – Orion Adrian Sep 26 '08 at 16:38
2

The first is using the value in level and THEN incrmenting it.

The latter is using level+1 as a passed variable.

Henry B
  • 7,947
  • 10
  • 42
  • 46
1

level++ returns the current value of level, then increments level. level+1 doesn't change level at all, but DoStuff is called with the value of (level + 1).

Markus Schnell
  • 1,075
  • 1
  • 10
  • 13
1
public DoStuff(int level)
{

  // DoStuff(level);
  DoStuff(level++);
  // level = level + 1;
  // here, level's value is 1 greater than when it came in
}

It actually increments the value of level.

public DoStuff(int level)
{
  // int iTmp = level + 1;
  // DoStuff(iTmp);
  DoStuff(level+1);
  // here, level's value hasn't changed
}

doesn't actually increment the value of level.

Not a huge problem before the function call, but after the function call, the values will be different.

Wes P
  • 9,622
  • 14
  • 41
  • 48
1

In level++ you are using postfix operator. This operator works after the variable is used. That is after it is put on the stack for the called function, it is incremented. On the other hand level + 1 is simple mathematical expression and it is evaluated and the result is passed to called function. If you want to increment the variable first and then pass it to called function, you can use prefix operator: ++level

TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
0

The first code snippet uses the post-operation increment operator, so the call is made as DoStuff(level);. If you want to use an increment operator here, use DoStuff(++level);.

Steve Moyer
  • 5,663
  • 1
  • 24
  • 34
0

level+1 sends whatever level+1 is to the function. level++ sends level to the function and then increments it.

You could do ++level and that would likely give you the results you want.

itsmatt
  • 31,265
  • 10
  • 100
  • 164
0

The first example uses the value of 'index', increments the value and updates 'index'.

The second example uses the value of 'index' plus 1 but does not change the content of 'index'.

So, depending on what you are wanting to do here, there could be some surprises in store!

paul
  • 13,312
  • 23
  • 81
  • 144
0

Whilst it is tempting to rewrite as:

DoStuff(++level);

I personally think this is less readable than incrementing the variable prior to the method call. As noted by a couple of the answers above, the following would be clearer:

level++;
DoStuff(level);
Chris Ballard
  • 3,771
  • 4
  • 28
  • 40
  • The pre-increment and post-increment operators are meant to add a level of conciseness to code, not necessarily readability. If you're shooting for readability, don't use this level of operator at all. Use level = level + 1; – Michael Meadows Sep 22 '08 at 12:42
  • I didn't say it was more concise, just aids readability. I don't agree with using level = level + 1, as it involves more typing :) - I think most people know what ++ does, but (as per original question) sometimes get confused about the ordering. – Chris Ballard Sep 24 '08 at 13:21
0

When you use a language that allows operator overloading, and '+ <integer>' has been defined to do something other than post- and prefix '++'.

Then again, I have only seen such abominations in school projects*, if you encounter that in the wild you probably have a really good, well-documented, reason.

[* a stack of integers, if I'm not mistaken. '++' and '--' pushed and popped, while '+' and '-' performed normal arithmetics]

Christoffer
  • 12,712
  • 7
  • 37
  • 53
0

To put it in the most simple way, ++var is a prefix operator and will increment the variables before the rest of the expression is evaluated. var++, a postfix operator, increments a variable after the rest of the expression is evaluated. And as others have mentioned of course, var+1 creates just a temporary variable (seperate in memory) which is initiated with var and incremented with constant 1.

MarioDS
  • 12,895
  • 15
  • 65
  • 121
-2

As far as my experience goes, the parameter expression is evaluated first, and gets a value of level. The variable itself is incremented before the function is called, because the compiler doesnt care whether you are using the expression as a parameter or otherwise... All it knows is that it should increment the value and get the old value as the result of the expression.

However in my opinion, code like this is really sloppy, since by trying to be clever, it makes you have to think twice about what is really happening.

rep_movsd
  • 6,675
  • 4
  • 30
  • 34