Is there an explanation for inline operators in “k += c += k += c;”?












18















What is the explanation for the result from the following 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 Visual Studio tells me that that value is not being used elsewhere)?



Why is k 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 the CIL, but I am not so profound in interpreting generated CIL 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 // I expect it 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??????









share|improve this question









New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 2





    You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

    – João Paulo Amorim
    9 hours ago






  • 23





    Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

    – UnhandledExcepSean
    9 hours ago






  • 3





    @AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

    – João Paulo Amorim
    9 hours ago






  • 3





    Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

    – Wai Ha Lee
    9 hours ago






  • 7





    "Doctor, doctor, it hurts when I do this!" "So don't DO that."

    – David Conrad
    8 hours ago
















18















What is the explanation for the result from the following 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 Visual Studio tells me that that value is not being used elsewhere)?



Why is k 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 the CIL, but I am not so profound in interpreting generated CIL 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 // I expect it 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??????









share|improve this question









New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 2





    You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

    – João Paulo Amorim
    9 hours ago






  • 23





    Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

    – UnhandledExcepSean
    9 hours ago






  • 3





    @AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

    – João Paulo Amorim
    9 hours ago






  • 3





    Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

    – Wai Ha Lee
    9 hours ago






  • 7





    "Doctor, doctor, it hurts when I do this!" "So don't DO that."

    – David Conrad
    8 hours ago














18












18








18


1






What is the explanation for the result from the following 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 Visual Studio tells me that that value is not being used elsewhere)?



Why is k 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 the CIL, but I am not so profound in interpreting generated CIL 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 // I expect it 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??????









share|improve this question









New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












What is the explanation for the result from the following 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 Visual Studio tells me that that value is not being used elsewhere)?



Why is k 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 the CIL, but I am not so profound in interpreting generated CIL 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 // I expect it 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??????






c# cil compound-assignment






share|improve this question









New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 57 mins ago









Peter Mortensen

13.6k1986111




13.6k1986111






New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 9 hours ago









Andrii KotliarovAndrii Kotliarov

966




966




New contributor




Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Andrii Kotliarov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








  • 2





    You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

    – João Paulo Amorim
    9 hours ago






  • 23





    Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

    – UnhandledExcepSean
    9 hours ago






  • 3





    @AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

    – João Paulo Amorim
    9 hours ago






  • 3





    Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

    – Wai Ha Lee
    9 hours ago






  • 7





    "Doctor, doctor, it hurts when I do this!" "So don't DO that."

    – David Conrad
    8 hours ago














  • 2





    You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

    – João Paulo Amorim
    9 hours ago






  • 23





    Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

    – UnhandledExcepSean
    9 hours ago






  • 3





    @AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

    – João Paulo Amorim
    9 hours ago






  • 3





    Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

    – Wai Ha Lee
    9 hours ago






  • 7





    "Doctor, doctor, it hurts when I do this!" "So don't DO that."

    – David Conrad
    8 hours ago








2




2





You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

– João Paulo Amorim
9 hours ago





You got different result because you split the function, k += c += k += c = 80 because the values of k and c stay the same in all the sums, so k += c += k += c is equal to 10 + 30 + 10 + 30

– João Paulo Amorim
9 hours ago




23




23





Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

– UnhandledExcepSean
9 hours ago





Interesting exercise, but, in practice, never write code chaining like that unless you want your coworkers to hate you. :)

– UnhandledExcepSean
9 hours ago




3




3





@AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

– João Paulo Amorim
9 hours ago





@AndriiKotliarov because k += c += k += c is 10 + 30 + 10 + 30, so, K receives all the values, and C gets only the last 3 arguments 30 + 10 + 30 = 70

– João Paulo Amorim
9 hours ago




3




3





Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

– Wai Ha Lee
9 hours ago





Also worth reading - Eric Lippert's answer to What is the difference between i++ and ++i?

– Wai Ha Lee
9 hours ago




7




7





"Doctor, doctor, it hurts when I do this!" "So don't DO that."

– David Conrad
8 hours ago





"Doctor, doctor, it hurts when I do this!" "So don't DO that."

– David Conrad
8 hours ago












3 Answers
3






active

oldest

votes


















33














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));


or expanded



     10   →   30   →   10 → 30   // operand evaluation order
k = k + (c = c + (k = k + c));
40 ← 10 30 // operator evaluation order
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.



The point is that (according to the C# spec) "Operands in an expression are evaluated from left to right" (the operands are the variables in our case). This is independent of the operator precedence which in this case dictates an execution order from right to left. (See comments to Eric Lippert's answer on this page).





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




IL_000c: dup



10 30 40 40




IL_000d: stloc.0      // k <-- 40



10 30 40




IL_000e: add



10 70




IL_000f: dup



10 70 70




IL_0010: stloc.1      // c <-- 70



10 70




IL_0011: add



80




IL_0012: stloc.0      // k <-- 80


Note that IL_000c: dup, 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.






share|improve this answer


























  • You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

    – Franck
    9 hours ago






  • 2





    Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

    – Eric Lippert
    6 hours ago











  • A console test in Release mode indeed shows that k is assigned twice if it is a property.

    – Olivier Jacot-Descombes
    6 hours ago



















7














First off, Henk and Olivier's answers are correct; I want to explain it in a slightly different way. Specifically, I want to address this point you made. You have this set of statements:



int k = 10;
int c = 30;
k += c += k += c;


And you then incorrectly conclude that this should give the same result as this set of statements:



int k = 10;
int c = 30;
k += c;
c += k;
k += c;


It is informative to see how you got that wrong, and how to do it right. The right way to break it down is like this.



First, rewrite the outermost +=



k = k + (c += k += c);


Second, rewrite the outermost +. I hope you agree that x = y + z must always be the same as "evaluate y to a temporary, evaluate z to a temporary, sum the temporaries, assign the sum to x". So let's make that very explicit:



int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;


Make sure that is clear, because this is the step you got wrong. When breaking down complex operations into simpler operation you must make sure that you do so slowly and carefully and do not skip steps. Skipping steps is where we make mistakes.



OK, now break down the assignment to t2, again, slowly and carefully.



int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;


The assignment will assign the same value to t2 as is assigned to c, so let's say that:



int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;


Great. Now break down the second line:



int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Great, we are making progress. Break down the assignment to t4:



int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Now break down the third line:



int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


And now we can look at the whole thing:



int k = 10;  // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80


So when we are done, k is 80 and c is 70.



Now let's look at how this is implemented in the IL:



int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3


Now this is a bit tricky:



int t4 = k + c; 
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum


We could have implemented the above as



ldloc.0      // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k


but we use the "dup" trick because it makes the code shorter and makes it easier on the jitter, and we get the same result. In general, the C# code generator tries to keep temporaries "ephemeral" on the stack as much as possible. If you find it easier to follow the IL with fewer ephemerals, turn optimizations off, and the code generator will be less aggressive.



We now have to do the same trick to get c:



int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.


and finally:



k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.


Since we do not need the sum for anything else, we do not dup it. The stack is now empty, and we're at the end of the statement.



The moral of the story is: when you are trying to understand a complicated program, always break down operations one at a time. Don't take short cuts; they will lead you astray.






share|improve this answer


























  • You have a stray semicolon in your third snippet.

    – Rogem
    6 hours ago











  • From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

    – Olivier Jacot-Descombes
    5 hours ago











  • @OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

    – Eric Lippert
    5 hours ago











  • @OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

    – Eric Lippert
    5 hours ago






  • 1





    @OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

    – Eric Lippert
    3 hours ago





















6














It boils down to: is the very first += applied to the original k or to the value that was computed more to the right ?



The answer is that although assignments bind from right to left, operations still proceed from left to right.



So the leftmost += is executing 10 += 70.






share|improve this answer


























  • This puts it nicely in a nut shell.

    – Aganju
    3 hours ago











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});






Andrii Kotliarov is a new contributor. Be nice, and check out our Code of Conduct.










draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54674728%2fis-there-an-explanation-for-inline-operators-in-k-c-k-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









33














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));


or expanded



     10   →   30   →   10 → 30   // operand evaluation order
k = k + (c = c + (k = k + c));
40 ← 10 30 // operator evaluation order
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.



The point is that (according to the C# spec) "Operands in an expression are evaluated from left to right" (the operands are the variables in our case). This is independent of the operator precedence which in this case dictates an execution order from right to left. (See comments to Eric Lippert's answer on this page).





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




IL_000c: dup



10 30 40 40




IL_000d: stloc.0      // k <-- 40



10 30 40




IL_000e: add



10 70




IL_000f: dup



10 70 70




IL_0010: stloc.1      // c <-- 70



10 70




IL_0011: add



80




IL_0012: stloc.0      // k <-- 80


Note that IL_000c: dup, 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.






share|improve this answer


























  • You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

    – Franck
    9 hours ago






  • 2





    Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

    – Eric Lippert
    6 hours ago











  • A console test in Release mode indeed shows that k is assigned twice if it is a property.

    – Olivier Jacot-Descombes
    6 hours ago
















33














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));


or expanded



     10   →   30   →   10 → 30   // operand evaluation order
k = k + (c = c + (k = k + c));
40 ← 10 30 // operator evaluation order
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.



The point is that (according to the C# spec) "Operands in an expression are evaluated from left to right" (the operands are the variables in our case). This is independent of the operator precedence which in this case dictates an execution order from right to left. (See comments to Eric Lippert's answer on this page).





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




IL_000c: dup



10 30 40 40




IL_000d: stloc.0      // k <-- 40



10 30 40




IL_000e: add



10 70




IL_000f: dup



10 70 70




IL_0010: stloc.1      // c <-- 70



10 70




IL_0011: add



80




IL_0012: stloc.0      // k <-- 80


Note that IL_000c: dup, 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.






share|improve this answer


























  • You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

    – Franck
    9 hours ago






  • 2





    Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

    – Eric Lippert
    6 hours ago











  • A console test in Release mode indeed shows that k is assigned twice if it is a property.

    – Olivier Jacot-Descombes
    6 hours ago














33












33








33







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));


or expanded



     10   →   30   →   10 → 30   // operand evaluation order
k = k + (c = c + (k = k + c));
40 ← 10 30 // operator evaluation order
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.



The point is that (according to the C# spec) "Operands in an expression are evaluated from left to right" (the operands are the variables in our case). This is independent of the operator precedence which in this case dictates an execution order from right to left. (See comments to Eric Lippert's answer on this page).





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




IL_000c: dup



10 30 40 40




IL_000d: stloc.0      // k <-- 40



10 30 40




IL_000e: add



10 70




IL_000f: dup



10 70 70




IL_0010: stloc.1      // c <-- 70



10 70




IL_0011: add



80




IL_0012: stloc.0      // k <-- 80


Note that IL_000c: dup, 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.






share|improve this answer















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));


or expanded



     10   →   30   →   10 → 30   // operand evaluation order
k = k + (c = c + (k = k + c));
40 ← 10 30 // operator evaluation order
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.



The point is that (according to the C# spec) "Operands in an expression are evaluated from left to right" (the operands are the variables in our case). This is independent of the operator precedence which in this case dictates an execution order from right to left. (See comments to Eric Lippert's answer on this page).





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




IL_000c: dup



10 30 40 40




IL_000d: stloc.0      // k <-- 40



10 30 40




IL_000e: add



10 70




IL_000f: dup



10 70 70




IL_0010: stloc.1      // c <-- 70



10 70




IL_0011: add



80




IL_0012: stloc.0      // k <-- 80


Note that IL_000c: dup, 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.







share|improve this answer














share|improve this answer



share|improve this answer








edited 3 hours ago

























answered 9 hours ago









Olivier Jacot-DescombesOlivier Jacot-Descombes

67.3k887140




67.3k887140













  • You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

    – Franck
    9 hours ago






  • 2





    Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

    – Eric Lippert
    6 hours ago











  • A console test in Release mode indeed shows that k is assigned twice if it is a property.

    – Olivier Jacot-Descombes
    6 hours ago



















  • You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

    – Franck
    9 hours ago






  • 2





    Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

    – Eric Lippert
    6 hours ago











  • A console test in Release mode indeed shows that k is assigned twice if it is a property.

    – Olivier Jacot-Descombes
    6 hours ago

















You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

– Franck
9 hours ago





You could add the final result with the numbers in the formula for even more complete : final is k = 10 + (30 + (10 + 30)) = 80 and that c final value is set in the first parenthesis which is c = 30 + (10 + 30) = 70.

– Franck
9 hours ago




2




2





Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

– Eric Lippert
6 hours ago





Indeed if k is a local then the dead store is almost certainly removed if optimizations are on, and preserved if they are not. An interesting question is whether the jitter is permitted to elide the dead store if k is a field, property, array slot, and so on; in practice I believe it does not.

– Eric Lippert
6 hours ago













A console test in Release mode indeed shows that k is assigned twice if it is a property.

– Olivier Jacot-Descombes
6 hours ago





A console test in Release mode indeed shows that k is assigned twice if it is a property.

– Olivier Jacot-Descombes
6 hours ago













7














First off, Henk and Olivier's answers are correct; I want to explain it in a slightly different way. Specifically, I want to address this point you made. You have this set of statements:



int k = 10;
int c = 30;
k += c += k += c;


And you then incorrectly conclude that this should give the same result as this set of statements:



int k = 10;
int c = 30;
k += c;
c += k;
k += c;


It is informative to see how you got that wrong, and how to do it right. The right way to break it down is like this.



First, rewrite the outermost +=



k = k + (c += k += c);


Second, rewrite the outermost +. I hope you agree that x = y + z must always be the same as "evaluate y to a temporary, evaluate z to a temporary, sum the temporaries, assign the sum to x". So let's make that very explicit:



int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;


Make sure that is clear, because this is the step you got wrong. When breaking down complex operations into simpler operation you must make sure that you do so slowly and carefully and do not skip steps. Skipping steps is where we make mistakes.



OK, now break down the assignment to t2, again, slowly and carefully.



int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;


The assignment will assign the same value to t2 as is assigned to c, so let's say that:



int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;


Great. Now break down the second line:



int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Great, we are making progress. Break down the assignment to t4:



int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Now break down the third line:



int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


And now we can look at the whole thing:



int k = 10;  // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80


So when we are done, k is 80 and c is 70.



Now let's look at how this is implemented in the IL:



int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3


Now this is a bit tricky:



int t4 = k + c; 
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum


We could have implemented the above as



ldloc.0      // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k


but we use the "dup" trick because it makes the code shorter and makes it easier on the jitter, and we get the same result. In general, the C# code generator tries to keep temporaries "ephemeral" on the stack as much as possible. If you find it easier to follow the IL with fewer ephemerals, turn optimizations off, and the code generator will be less aggressive.



We now have to do the same trick to get c:



int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.


and finally:



k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.


Since we do not need the sum for anything else, we do not dup it. The stack is now empty, and we're at the end of the statement.



The moral of the story is: when you are trying to understand a complicated program, always break down operations one at a time. Don't take short cuts; they will lead you astray.






share|improve this answer


























  • You have a stray semicolon in your third snippet.

    – Rogem
    6 hours ago











  • From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

    – Olivier Jacot-Descombes
    5 hours ago











  • @OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

    – Eric Lippert
    5 hours ago











  • @OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

    – Eric Lippert
    5 hours ago






  • 1





    @OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

    – Eric Lippert
    3 hours ago


















7














First off, Henk and Olivier's answers are correct; I want to explain it in a slightly different way. Specifically, I want to address this point you made. You have this set of statements:



int k = 10;
int c = 30;
k += c += k += c;


And you then incorrectly conclude that this should give the same result as this set of statements:



int k = 10;
int c = 30;
k += c;
c += k;
k += c;


It is informative to see how you got that wrong, and how to do it right. The right way to break it down is like this.



First, rewrite the outermost +=



k = k + (c += k += c);


Second, rewrite the outermost +. I hope you agree that x = y + z must always be the same as "evaluate y to a temporary, evaluate z to a temporary, sum the temporaries, assign the sum to x". So let's make that very explicit:



int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;


Make sure that is clear, because this is the step you got wrong. When breaking down complex operations into simpler operation you must make sure that you do so slowly and carefully and do not skip steps. Skipping steps is where we make mistakes.



OK, now break down the assignment to t2, again, slowly and carefully.



int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;


The assignment will assign the same value to t2 as is assigned to c, so let's say that:



int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;


Great. Now break down the second line:



int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Great, we are making progress. Break down the assignment to t4:



int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Now break down the third line:



int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


And now we can look at the whole thing:



int k = 10;  // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80


So when we are done, k is 80 and c is 70.



Now let's look at how this is implemented in the IL:



int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3


Now this is a bit tricky:



int t4 = k + c; 
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum


We could have implemented the above as



ldloc.0      // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k


but we use the "dup" trick because it makes the code shorter and makes it easier on the jitter, and we get the same result. In general, the C# code generator tries to keep temporaries "ephemeral" on the stack as much as possible. If you find it easier to follow the IL with fewer ephemerals, turn optimizations off, and the code generator will be less aggressive.



We now have to do the same trick to get c:



int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.


and finally:



k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.


Since we do not need the sum for anything else, we do not dup it. The stack is now empty, and we're at the end of the statement.



The moral of the story is: when you are trying to understand a complicated program, always break down operations one at a time. Don't take short cuts; they will lead you astray.






share|improve this answer


























  • You have a stray semicolon in your third snippet.

    – Rogem
    6 hours ago











  • From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

    – Olivier Jacot-Descombes
    5 hours ago











  • @OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

    – Eric Lippert
    5 hours ago











  • @OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

    – Eric Lippert
    5 hours ago






  • 1





    @OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

    – Eric Lippert
    3 hours ago
















7












7








7







First off, Henk and Olivier's answers are correct; I want to explain it in a slightly different way. Specifically, I want to address this point you made. You have this set of statements:



int k = 10;
int c = 30;
k += c += k += c;


And you then incorrectly conclude that this should give the same result as this set of statements:



int k = 10;
int c = 30;
k += c;
c += k;
k += c;


It is informative to see how you got that wrong, and how to do it right. The right way to break it down is like this.



First, rewrite the outermost +=



k = k + (c += k += c);


Second, rewrite the outermost +. I hope you agree that x = y + z must always be the same as "evaluate y to a temporary, evaluate z to a temporary, sum the temporaries, assign the sum to x". So let's make that very explicit:



int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;


Make sure that is clear, because this is the step you got wrong. When breaking down complex operations into simpler operation you must make sure that you do so slowly and carefully and do not skip steps. Skipping steps is where we make mistakes.



OK, now break down the assignment to t2, again, slowly and carefully.



int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;


The assignment will assign the same value to t2 as is assigned to c, so let's say that:



int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;


Great. Now break down the second line:



int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Great, we are making progress. Break down the assignment to t4:



int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Now break down the third line:



int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


And now we can look at the whole thing:



int k = 10;  // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80


So when we are done, k is 80 and c is 70.



Now let's look at how this is implemented in the IL:



int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3


Now this is a bit tricky:



int t4 = k + c; 
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum


We could have implemented the above as



ldloc.0      // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k


but we use the "dup" trick because it makes the code shorter and makes it easier on the jitter, and we get the same result. In general, the C# code generator tries to keep temporaries "ephemeral" on the stack as much as possible. If you find it easier to follow the IL with fewer ephemerals, turn optimizations off, and the code generator will be less aggressive.



We now have to do the same trick to get c:



int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.


and finally:



k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.


Since we do not need the sum for anything else, we do not dup it. The stack is now empty, and we're at the end of the statement.



The moral of the story is: when you are trying to understand a complicated program, always break down operations one at a time. Don't take short cuts; they will lead you astray.






share|improve this answer















First off, Henk and Olivier's answers are correct; I want to explain it in a slightly different way. Specifically, I want to address this point you made. You have this set of statements:



int k = 10;
int c = 30;
k += c += k += c;


And you then incorrectly conclude that this should give the same result as this set of statements:



int k = 10;
int c = 30;
k += c;
c += k;
k += c;


It is informative to see how you got that wrong, and how to do it right. The right way to break it down is like this.



First, rewrite the outermost +=



k = k + (c += k += c);


Second, rewrite the outermost +. I hope you agree that x = y + z must always be the same as "evaluate y to a temporary, evaluate z to a temporary, sum the temporaries, assign the sum to x". So let's make that very explicit:



int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;


Make sure that is clear, because this is the step you got wrong. When breaking down complex operations into simpler operation you must make sure that you do so slowly and carefully and do not skip steps. Skipping steps is where we make mistakes.



OK, now break down the assignment to t2, again, slowly and carefully.



int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;


The assignment will assign the same value to t2 as is assigned to c, so let's say that:



int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;


Great. Now break down the second line:



int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Great, we are making progress. Break down the assignment to t4:



int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


Now break down the third line:



int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;


And now we can look at the whole thing:



int k = 10;  // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80


So when we are done, k is 80 and c is 70.



Now let's look at how this is implemented in the IL:



int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3


Now this is a bit tricky:



int t4 = k + c; 
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum


We could have implemented the above as



ldloc.0      // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k


but we use the "dup" trick because it makes the code shorter and makes it easier on the jitter, and we get the same result. In general, the C# code generator tries to keep temporaries "ephemeral" on the stack as much as possible. If you find it easier to follow the IL with fewer ephemerals, turn optimizations off, and the code generator will be less aggressive.



We now have to do the same trick to get c:



int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.


and finally:



k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.


Since we do not need the sum for anything else, we do not dup it. The stack is now empty, and we're at the end of the statement.



The moral of the story is: when you are trying to understand a complicated program, always break down operations one at a time. Don't take short cuts; they will lead you astray.







share|improve this answer














share|improve this answer



share|improve this answer








edited 6 hours ago

























answered 7 hours ago









Eric LippertEric Lippert

540k14610581939




540k14610581939













  • You have a stray semicolon in your third snippet.

    – Rogem
    6 hours ago











  • From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

    – Olivier Jacot-Descombes
    5 hours ago











  • @OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

    – Eric Lippert
    5 hours ago











  • @OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

    – Eric Lippert
    5 hours ago






  • 1





    @OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

    – Eric Lippert
    3 hours ago





















  • You have a stray semicolon in your third snippet.

    – Rogem
    6 hours ago











  • From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

    – Olivier Jacot-Descombes
    5 hours ago











  • @OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

    – Eric Lippert
    5 hours ago











  • @OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

    – Eric Lippert
    5 hours ago






  • 1





    @OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

    – Eric Lippert
    3 hours ago



















You have a stray semicolon in your third snippet.

– Rogem
6 hours ago





You have a stray semicolon in your third snippet.

– Rogem
6 hours ago













From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

– Olivier Jacot-Descombes
5 hours ago





From the C# spec for Simple assignment I cannot tell that the old value of k would be used. Can you point me to the right spot in the spec?

– Olivier Jacot-Descombes
5 hours ago













@OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

– Eric Lippert
5 hours ago





@OlivierJacot-Descombes: I'm not sure why you're looking at simple assignment; the question is about compound assignment. Can you clarify the question?

– Eric Lippert
5 hours ago













@OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

– Eric Lippert
5 hours ago





@OlivierJacot-Descombes: There is nowhere in the spec that says that the old value is used. What the spec says is that x += y is the same as x = x + y except that any side effects of computing x only happen once. The spec for x + y says that this is evaluated as "evaluate x, evaluate y, evaluate their sum". Does that answer your question?

– Eric Lippert
5 hours ago




1




1





@OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

– Eric Lippert
3 hours ago







@OlivierJacot-Descombes: That's exactly right. Precedence and associativity have nothing whatsoever to do with the order in which subexpressions are evaluated, other than the fact that precedence and associativity determine where the subexpression boundaries are. Subexpressions are evaluated left-to-right.

– Eric Lippert
3 hours ago













6














It boils down to: is the very first += applied to the original k or to the value that was computed more to the right ?



The answer is that although assignments bind from right to left, operations still proceed from left to right.



So the leftmost += is executing 10 += 70.






share|improve this answer


























  • This puts it nicely in a nut shell.

    – Aganju
    3 hours ago
















6














It boils down to: is the very first += applied to the original k or to the value that was computed more to the right ?



The answer is that although assignments bind from right to left, operations still proceed from left to right.



So the leftmost += is executing 10 += 70.






share|improve this answer


























  • This puts it nicely in a nut shell.

    – Aganju
    3 hours ago














6












6








6







It boils down to: is the very first += applied to the original k or to the value that was computed more to the right ?



The answer is that although assignments bind from right to left, operations still proceed from left to right.



So the leftmost += is executing 10 += 70.






share|improve this answer















It boils down to: is the very first += applied to the original k or to the value that was computed more to the right ?



The answer is that although assignments bind from right to left, operations still proceed from left to right.



So the leftmost += is executing 10 += 70.







share|improve this answer














share|improve this answer



share|improve this answer








edited 9 hours ago









Wai Ha Lee

5,860123964




5,860123964










answered 9 hours ago









Henk HoltermanHenk Holterman

209k22232402




209k22232402













  • This puts it nicely in a nut shell.

    – Aganju
    3 hours ago



















  • This puts it nicely in a nut shell.

    – Aganju
    3 hours ago

















This puts it nicely in a nut shell.

– Aganju
3 hours ago





This puts it nicely in a nut shell.

– Aganju
3 hours ago










Andrii Kotliarov is a new contributor. Be nice, and check out our Code of Conduct.










draft saved

draft discarded


















Andrii Kotliarov is a new contributor. Be nice, and check out our Code of Conduct.













Andrii Kotliarov is a new contributor. Be nice, and check out our Code of Conduct.












Andrii Kotliarov is a new contributor. Be nice, and check out our Code of Conduct.
















Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54674728%2fis-there-an-explanation-for-inline-operators-in-k-c-k-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown