Can anyone explain the result from this operation:
k += c += k += c;
I was trying to understand the output result from the following code:
int k = 10; int c = 30; k += c += k += c; //k=80 instead of 110 //c=70
and currently I am struggling with understanding why the result for "k" is 80. Why is assigning k=40 not working (actually VS tells me that that value is not being used elsewhere)?
Can anyone help me to understand why k is 80 and not 110?
If I split the operation to:
k+=c; c+=k; k+=c;
the result is k=110.
I was trying to look through IL, but I am not so profound in interpreting generated IL and can not get a few details:
// [11 13 - 11 24] IL_0001: ldc.i4.s 10 IL_0003: stloc.0 // k // [12 13 - 12 24] IL_0004: ldc.i4.s 30 IL_0006: stloc.1 // c // [13 13 - 13 30] IL_0007: ldloc.0 // k expect to be 10 IL_0008: ldloc.1 // c IL_0009: ldloc.0 // k why do we need the second load? IL_000a: ldloc.1 // c IL_000b: add //expect to be 40 IL_000c: dup //what for? IL_000d: stloc.0 // k - expected to be 40 IL_000e: add IL_000f: dup //I presume the "magic" happens here IL_0010: stloc.1 // c = 70 IL_0011: add IL_0012: stloc.0 // k = 80??????
An operation like
a op= b; is equivalent to
a = a op b;. An assignment can be used as statement or as expression, while as expression it yields the assigned value. Your statement
k += c += k += c;
can be translated to the equivalent one (since the assignment operator is right-associative)
k += (c += (k += c));
k = k + (c = c + (k = k + c)); 40 ← 10 30 70 ← 30 40 80 ← 10 70
Where during the whole evaluation the old values of the involved variables are used. This is especially true for the value of
k (see my review of the IL below and the link Wai Ha Lee provided). Therefore, you are not getting 70 + 40 (new value of
k) = 110, but 70 + 10 (old value of
k) = 80.
Now let's look at the IL. IL assumes a stack based virtual machine, i.e. it does not use registers.
IL_0007: ldloc.0 // k (is 10) IL_0008: ldloc.1 // c (is 30) IL_0009: ldloc.0 // k (is 10) IL_000a: ldloc.1 // c (is 30)
The stack now looks like this (from left to right; top of stack is right)
10 30 10 30
IL_000b: add // pops the 2 top (right) positions, adds them and pushes the sum
10 30 40
10 30 40 40
IL_000d: stloc.0 // k <-- 40
10 30 40
10 70 70
IL_0010: stloc.1 // c <-- 70
IL_0012: stloc.0 // k <-- 80
IL_000d: stloc.0, i.e. the first assignment to
k , could be optimized away. Probably this is done by the jitter when converting IL to machine code.
Note also that all the values required by the calculation are either pushed to the stack before any assignment is made or are calculated from these values. Assigned values (by
stloc) are never re-used during this evaluation.
stloc pops the top of the stack.