views:

1816

answers:

14

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)
+27  A: 

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
++level also produces a different behaviour. See the edits to my question.
Richard Dorman
+2  A: 

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

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

PintSizedCat
A: 

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
+1  A: 

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
+29  A: 

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
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
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
So what's intuitive and easy about this? :-)DoMoreStuff(++level, ++level);
Frederik Slijkerman
I think that's classed as 'trying to get cute' :)
workmad3
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
That's a fairly subtle distinction for a local variable, though
Simon Buchan
The 2 are equivalent for the current example (apart from new temporary creation of course). More complicated examples would change this though.
workmad3
I have to agree with Orion.This answer demonstrates the point slightly but is missleading in terms of what actually happens.
PintSizedCat
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
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
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
@Herms: It isn't supposed to be equivalent to the original question.
Frederik Slijkerman
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
You're right. Fixed it.
Frederik Slijkerman
A: 

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
++level produces a different result. See the stack traces in my original question.
Richard Dorman
A: 

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
example uses 'level' not index. Suggest you edit this reply to follow?
workmad3
A: 

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
+12  A: 

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;


and looking at the stack traces for the various behavior:

// level++

DoStuff(int level = 1) //original call
DoStuff(int level = 1) //level = 2 after call
DoStuff(int level = 1) //level = 2 after call
DoStuff(int level = 1) //level = 2 after call

// ++level

DoStuff(int level = 1) //original call
DoStuff(int level = 2) //level = 2 after call
DoStuff(int level = 3) //level = 3 after call
DoStuff(int level = 4) //level = 4 after call

// level + 1

DoStuff(int level = 1) //original call
DoStuff(int level = 2) //level = 1 after call
DoStuff(int level = 3) //level = 2 after call
DoStuff(int level = 4) //level = 3 after call


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
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
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.
PintSizedCat
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
There's a mistake in your first code sample. temp is declared but never used.
Matt Howells
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
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
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
+42  A: 

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
Except your example for `DoStuff(a++)` is wrong. It should be: int temp = a; a = a + 1; DoStuff(temp);
Orion Adrian
@Orion Adrian: No, the example isn't wrong.
vitule
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
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
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
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
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
Kimmo Puputti
@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
+1  A: 
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
You got the first one the wrong way round: it will first call DoStuff(level) and afterwards increment level.
Sam
Woops. Haha, hasty answer on my part :-p
Wes P
A: 

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
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
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
A: 

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
+1  A: 

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