2

This works, k increments:

k = 0;
k = ( false condition here ) ? 0 : k+=1;

This works, k increments:

k = 0;
k = ( false condition here ) ? 0 : ++k;

This does not work, k is always 0:

k = 0;
k = ( false condition here ) ? 0 : k++;

Can someone explain what's going on under the hood?

EDIT: I don't need alternative ways to write this. I don't care if this can be written in a simpler way.

In a for loop either i++ or ++i works. Why the behaviour here is different?

Morwenn
  • 21,684
  • 12
  • 93
  • 152
Mauro Sampietro
  • 2,739
  • 1
  • 24
  • 50
  • 5
    possible duplicate of [What is the difference between ++i and i++](http://stackoverflow.com/questions/24853/what-is-the-difference-between-i-and-i) – PiousVenom Mar 20 '15 at 13:31
  • 6
    Make it easier... `int k = 0; k = k++;` k is still 0. No ternary needed – xanatos Mar 20 '15 at 13:33
  • Did your lecturer teach you that k++ is equivalent to k+=1? I'm not joking. I've been taught this. – Lews Therin Mar 20 '15 at 13:34
  • by the way: your code will become much more readable/understandable by not using ternary expressions or conditions with side effects... – eFloh Mar 20 '15 at 13:37
  • 1
    Whatever happens, don't ever mix an increment with pretty much anything else. It just confuses the hell out of people, is hard to read and pretty much useless. `k++` or `++k` alone is fine. Mix it with assignations, arithmetics or, god forbids, ternary or null-coalescing operators and all hells break loose. Short version: it hurts when you do that. So just don't do it. – Falanwe Mar 20 '15 at 13:59
  • 1
    possible duplicate of [What is the difference between i++ and ++i?](http://stackoverflow.com/questions/3346450/what-is-the-difference-between-i-and-i) – default Mar 20 '15 at 14:45
  • Look into "Sequence points" and [this question](http://stackoverflow.com/q/3346450/238902). That should describe what you are seeing. – default Mar 20 '15 at 14:46
  • Others have explained. But in a realistic scenario, what you want to use could be `k = (condition) ? 0 : k + 1;`. – Jeppe Stig Nielsen Mar 26 '15 at 09:26

4 Answers4

1

If you want to know what happens under the hood we can look at IL level. Before that I think it is worth it looking at the use of the ++ operator as suggested by xanatos.

Anyway let's have a look at the generated IL for the second case. Please look at the comment on the right:

int k = 0; 
k =  false ? 0 : ++k;

IL_0001:  ldc.i4.0    // Allocate space for int k
IL_0002:  stloc.0     // assign 0 to k
IL_0003:  ldloc.0     // load k on top of the evaluation stack     --> our stack is [k]
IL_0004:  ldc.i4.1    // load value 1 at location 1 for variable k --> [k 1]
IL_0005:  add         // Pops and add the top two values on the evaluation stack, and push the result onto the stack. our stack is --> [1]
IL_0006:  dup         // Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. which in our case is 1 --> [1 1]
IL_0007:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) --> [1]
IL_0008:  stloc.0     // same again, the stack is empty now --> []
IL_0009:  ret 

As you can see the last two stloc.0 assigned the two 1 from the stack to k. In fact we had two assignments if you think about it. One was for ++k which is and the other to assign the result of the ternary operation. As you said this produces 1. Let's have a look at your last case which produces 0:

int k = 0; 
k =  false ? 0 : k++;

IL_0001:  ldc.i4.0    // Allocate space for int k
IL_0002:  stloc.0     // assign 0 to k
IL_0003:  ldloc.0     // load k on top of the evaluation stack     --> our stack is [k]
IL_0004:  dup         // Copies the current topmost value on the evaluation stack, and then pushes the copy onto the evaluation stack. which in our case is 1 --> [k k] in this case k is still 0!
IL_0005:  ldc.i4.1    // load value 1 at location 1 for variable k --> [k k 1]
IL_0006:  add         // Pops and add the top two values on the evaluation stack, and push the result onto the stack. our stack is --> [k 1] // because k+1 is equal 1
IL_0007:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) --> [1]
IL_0008:  stloc.0     // Pop the top value on the stack at location 0 (e.g. assign it to k) but in this case k is still zero!!!!! --> []

As you can see through the comments in the IL the two stloc.0 instruction assign eventually the original value of k (which was 0) to k itself. This is the reason why in this very case you get 0 and not 1.

I'm not giving a solution to your problem but only an explanation of how, at a level below in MSIL, these "simple" operations are handled.

Hope it helps.

codingadventures
  • 2,924
  • 2
  • 19
  • 36
0

Let's make it easier:

int x = 1;
x = x++;
// x still 1

Why?

For operator precedence, first the ++ operator will be executed, then the = assignment

Let's look at the ++. From https://msdn.microsoft.com/en-us/library/aa691363%28v=vs.71%29.aspx

If x is classified as a variable:

x is evaluated to produce the variable 

The value of x is saved. 
**1 is saved**

The selected operator is invoked with the saved value of x as its argument. 
**2 is produced**

The value returned by the operator is stored in the location given by the evaluation of x. 
**2 is put into x**

The saved value of x becomes the result of the operation.
**1 is returned**

and now we have the assignment: x = 1. So x is assigned twice, once with 2 and once with 1

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • How about a one liner that += doesn't use a temporary? – Lews Therin Mar 20 '15 at 13:48
  • @LewsTherin If you think about it, a `+=` operator must first add, then assign. And then, as all the other assignment operators, the assigned value is returned (for example, with `x = y = 5`, both `x` and `y` are `5` at the end). So `x = (x+=1)` is equivalent to writing `x = (x = (x + 1))`, with two assignment of the value 2 (if x == 1 at the beginning) – xanatos Mar 20 '15 at 13:51
  • 1
    And if you want a mind bender, consider the C code `int i = 0; printf("%d %d\n", i, i++);` for which the output is defined solely by your compiler and/or compiler settings. – plinth Mar 20 '15 at 13:52
  • @plinth So it's a good thing in this case the OP is only asking for the C# behavior, since the MS and Mono compilers behave the exact same way – thorkia Mar 20 '15 at 14:22
  • 2
    @thorkia : it's a good thing MS and Mono compilers behave the same way, since the behavior is clearly defined by the C# language specifications. There is no room for interpretation in these C# examples (opposite to the C one, which is clearly undefined behavior). – Falanwe Mar 20 '15 at 14:30
0

i++ and ++i work differently in a for loop.

Consider the following for loop definition:

for (int i=0; i<1; ++i)

can be written as:

for (int i=0; 1 >= i ; ++i)

This is the equivalent of writing:

for (int i=0; 1 >= ++i; /*No function here that increments i*/)

In this case, the increment is executed before the comparison, and the contents of the loop will never be executed.

Now consider this loop:

for (int i=0; i<1; i++)

can be written as:

for (int i=0; 1 >= i ; i++)

This is the equivalent of writing:

for (int i=0; 1 >= i++; /*No function here that increments i*/)

In this case, the increment is executed after the comparison, and the contents of the loop will be executed once. At the end of the loop, i will have a value of one.

You can test this by doing the following:

int i;
for (i=0; i<1; i++)
{
  Console.WriteLine("i in the loop has a value of: " + i);
}
Console.WriteLine("i after the loop has a value of: " + i);

The same rules apply to ternary operators as well.

This case will have the increment execute before assigning the value to k, and k will be 26:

int k=25;
k= (false) ? 0 : ++k

And this case will have increment executed after assigning the value to k, and k will be 25:

int k=25;
k = (false) ? 0 : k++;

k+=1 is not the same as a ++ operator, and k-=1 is not the same as a -- operator. k+=1 is actually 2 operations written concisely:

k = k+1

It first takes the value of k, adds one to it in memory, then assigns the value back to k.

So in the ternary example:

int k=25;
k = (false) ? 0 : k+=1;

k will be 26. Assignment operators are evaluated from right to left. So the compiler will first evaluate k+=1 - making k 26. Then the ternary comparison will execute, then 26 will be assigned to k.

Summary:

++/-- are special operators and where you put it affects when in the evaluation of an expression it is executed. ++x or --x - the ++ or -- will be evaluated before any comparisons. x++ or x-- - the ++ or -- will be evaluated after any comparisons

thorkia
  • 1,972
  • 1
  • 20
  • 26
  • `for (int i=0; 1 >= i ; ++i)` is executed twice. Example [here](https://dotnetfiddle.net/BJyonS). It is **not** equivalent to `for (int i=0; 1 >= ++i;)`. – default Mar 20 '15 at 15:56
0

Watch out when you mix post increments and assignments. The difference between pre and post increment arise when you use them with assignments.

Post increment (++) function implementation returns the original untouched input value (it saves a temp copy) and increments the input:

i = 0
i = i++

post increment ++ function will take as input i=0 and will increment it. At this point i will actually be 1 but then the post increment function returns the original value which is 0 so i will be reassigned and the final value will be 0

In a classic for loop for(int i=0; i < val; i++ ) using i++ or ++i won't make a difference since no assignment occurs while incrementing. This for loop can cause you headaches instead for(int i=0; i < val; i=i++ )

Mauro Sampietro
  • 2,739
  • 1
  • 24
  • 50