Is there an explanation for inline operators in “k += c += k += c;”?
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
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.
|
show 4 more comments
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
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
|
show 4 more comments
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
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
c# cil compound-assignment
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.
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
|
show 4 more comments
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
|
show 4 more comments
3 Answers
3
active
oldest
votes
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.
You could add the final result with the numbers in the formula for even more complete : final isk = 10 + (30 + (10 + 30)) = 80and thatcfinal value is set in the first parenthesis which isc = 30 + (10 + 30) = 70.
– Franck
9 hours ago
2
Indeed ifkis 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 ifkis 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 thatkis assigned twice if it is a property.
– Olivier Jacot-Descombes
6 hours ago
add a comment |
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.
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 ofkwould 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 thatx += yis the same asx = x + yexcept that any side effects of computingxonly happen once. The spec forx + ysays 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
|
show 5 more comments
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.
This puts it nicely in a nut shell.
– Aganju
3 hours ago
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
You could add the final result with the numbers in the formula for even more complete : final isk = 10 + (30 + (10 + 30)) = 80and thatcfinal value is set in the first parenthesis which isc = 30 + (10 + 30) = 70.
– Franck
9 hours ago
2
Indeed ifkis 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 ifkis 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 thatkis assigned twice if it is a property.
– Olivier Jacot-Descombes
6 hours ago
add a comment |
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.
You could add the final result with the numbers in the formula for even more complete : final isk = 10 + (30 + (10 + 30)) = 80and thatcfinal value is set in the first parenthesis which isc = 30 + (10 + 30) = 70.
– Franck
9 hours ago
2
Indeed ifkis 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 ifkis 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 thatkis assigned twice if it is a property.
– Olivier Jacot-Descombes
6 hours ago
add a comment |
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.
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.
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 isk = 10 + (30 + (10 + 30)) = 80and thatcfinal value is set in the first parenthesis which isc = 30 + (10 + 30) = 70.
– Franck
9 hours ago
2
Indeed ifkis 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 ifkis 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 thatkis assigned twice if it is a property.
– Olivier Jacot-Descombes
6 hours ago
add a comment |
You could add the final result with the numbers in the formula for even more complete : final isk = 10 + (30 + (10 + 30)) = 80and thatcfinal value is set in the first parenthesis which isc = 30 + (10 + 30) = 70.
– Franck
9 hours ago
2
Indeed ifkis 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 ifkis 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 thatkis 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
add a comment |
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.
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 ofkwould 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 thatx += yis the same asx = x + yexcept that any side effects of computingxonly happen once. The spec forx + ysays 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
|
show 5 more comments
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.
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 ofkwould 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 thatx += yis the same asx = x + yexcept that any side effects of computingxonly happen once. The spec forx + ysays 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
|
show 5 more comments
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.
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.
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 ofkwould 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 thatx += yis the same asx = x + yexcept that any side effects of computingxonly happen once. The spec forx + ysays 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
|
show 5 more comments
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 ofkwould 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 thatx += yis the same asx = x + yexcept that any side effects of computingxonly happen once. The spec forx + ysays 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
|
show 5 more comments
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.
This puts it nicely in a nut shell.
– Aganju
3 hours ago
add a comment |
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.
This puts it nicely in a nut shell.
– Aganju
3 hours ago
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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.
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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