Two posers for you
What is the output of the following code, and why?
[Update: By “why“, I mean “what part of the C# spec controls this behavior?“]
class Test
{
public static void Main()
{
int z = 2;
z += ++z+5*z++;
System.Console.WriteLine("Yak! {0}", z);
}
}
What about this code?
class Test
{
public static void Main()
{
int z = 2;
z += (5*z++)+(++z);
System.Console.WriteLine("Yak! {0}", z);
}
}
Extra credit: What is the defined output for C++, and why?
Comments
- Anonymous
April 19, 2004
The C++ output is implementation defined, because the C++ standard doesn't specify when in an expression the "set" portion of any increment instructions should be executed. - Anonymous
April 19, 2004
The answer is "it doesn't matter" right?
Because no-one would be evil enough to write a statement that used the ++ operator in an expression!
Right? Seriously, you guys.. :) - Anonymous
April 19, 2004
Thanks for that Eric... very intruiging :-)
You failed my code review though! - Anonymous
April 19, 2004
Is it "Yak! 28"? - Anonymous
April 19, 2004
20 and 16.
It apears that in both cases the += is using the value of Z at the start of the expression. - Anonymous
April 19, 2004
Poser one : the guy pretending to be programmer.
Poser two : the senior who fixed it into the second version.
The output of the code might be a job opening but is more likely to produce a lecture.
http://weblogs.asp.net/andrewseven/articles/Ramblings.aspx - Anonymous
April 19, 2004
Let me clarify....
It apears that in both cases, variable += expression is doing this...
1. get the value of "variable", store it in a temp register
2. evaluate expression
3. add the evaluated expresion to the value of Z in the temp register
4. set the value of Z with the value from the temp register - Anonymous
April 19, 2004
The first is 20
z = 2 + 3 + 5(3) = 20
The second is 16
z = (10 + 1)+(2)+ 3 - Anonymous
April 19, 2004
Hang on, let me paste it in Visual Studio... :) - Anonymous
April 19, 2004
My SWAG is that the pre-increment expression is firing after all of the orders of operation, so you get something like this:
int z = 2;
z += 2/++z/ + 5 * 3 /z++/;
++z;
System.Console.WriteLine("Yak! {0}", z);
I won't bother with the others unless I find out I'm on the right track. Fun stuff! - Anonymous
April 19, 2004
My SWAG is that the pre-increment expression is firing after all of the orders of operation, so you get something like this:
int z = 2;
z += 2/++z/ + 5 * 3 /z++/;
++z;
System.Console.WriteLine("Yak! {0}", z);
I won't bother with the others unless I find out I'm on the right track. Fun stuff! - Anonymous
April 19, 2004
Holy double post batman - sorry about that. OK, maybe I'm chasing up the wrong tree again, but I'm thinking that the second is another order of operations vs. increment/decrement expression:
int z = 2;
z += (5 * /z/2) /++operator/+ 1 + /value of z/(2);
++z;//then this happens??? - Anonymous
April 19, 2004
Here is what I found in the C# spec that governs the rules for this problem.
Sec 7.13.2 Compound Assignment states "If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once"
Sec 7.2 Operators states "The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators"
Sec 7.2.1 Operator precedence and associativity states "Except for the assignment operators, all binary operators are left-associative, meaning that operations are performed from left to right."
Sec 7.6.1 Prefix operator states "The result of ++x is the value of x after the operation"
Sec 7.5.9 Postfix operator states "The result of x++ is the value of x before the operation"
Working through the expression using the precedence table in section 7.2.1
z += ++z + 5 * z++;
z = z + (++z + 5 * z++); by 7.13.2
z = 2 + (++z + 5 * z++);
z = 2 + (++2 + 5 * z++);
z = 2 + (3 + 5 * z++); // z has value 3 now
z = 2 + (3 + 5 * 3++);
z = 2 + (3 + 5 * 3); // z has value 4 now
z = 2 + (3 + 15);
z = 2 + (18);
z = 20; // z has value 20 now
So the output is "Yak! 20"
Looking at the second expression
z += (5 * z++) + (++z);
z = z + ((5 * z++) + (++z));
z = 2 + ((5 * z++) + (++z));
z = 2 + ((5 * 2++) + (++z));
z = 2 + ((5 * 2) + (++z)); // z has value 3 now
z = 2 + ((10) + (++z));
z = 2 + (10 + (++3));
z = 2 + (10 + (4)); // z has value 4 now
z = 2 + (14);
z = 16; // z has value 16 now
So the output is "Yak! 16"
As far as the in C++ it is undefined because to the order of evaluation is undefined, from section 6.2.2 Evaluation Order in Programming Languages Third Edition by Bjarne Stroustrup. That section also states that it is undefined because better code can be generated in the absence of restrictions on expression evaluation order.
Wes - Anonymous
April 19, 2004
Sorry the C++ book is "The C++ Programming Language" Third Edition. Just in case someone didn't know. :) - Anonymous
April 19, 2004
16 each? - Anonymous
April 19, 2004
The comment has been removed - Anonymous
April 19, 2004
Isn't it funny that z += z++; produces the same result as z += z;
;) - Anonymous
April 19, 2004
Eric,
frankly: it doesn't matter. code like that should not be written (and i know that you know). write-only code like that needs some serious rewriting. :-)
WM_MY0.02$
thomas woelfer - Anonymous
April 20, 2004
Wes is right about how the expression gets evaluated. What's interesting though is that the order of evaluation doesn't quite seem to match what one would expect from the chart in 7.2.1. Lets take a simpler example by removing the compound expression and comparing the two following expressions:
- [1] -
int z = 2;
int y = z + ++z;
- or [2] -
int z = 2;
int y = ++z + z;
According to the chart, primary-expressions are evaluated first, and then unary-expressions after. A primary-expression can be a primary-no-array-creation-expression, which can be a simple-name, which is an identifier (such as z). A unary-expression comes after, which could be a pre-increment-expression (such as ++z). Therefore the statements should be evaluated as follows:
[1]
x = z + ++z; // start
x = 2 + ++z; // primary-expression evaluated
x = 2 + 3; // unary-expression evaluated, z is now 3
x = 5; // what you would expect
[2]
x = ++z + z; // start
x = ++z + 2; // primary-expression evaluated
x = 3 + 2; // unary-expression evaluated, z is now 3
x = 5; // same answer as in [1]
If you run this through version 7.10.3052.4 of the C# compiler you'll actually get something different for [2]:
x = ++z + z; // start
x = 3 + z; // unary-expression evaluated first! z is now 3
x = 3 + 3; // the primary-expression 'z' is evaluated which is now 3
x = 6; // Yak!
Hopefully I'm just misinterpreting the spec somehow, but shouldn't simple-name expressions such as 'z' be evaluated before unary-expressions such as ++z?
-Kael - Anonymous
April 20, 2004
Kael,
Actually I don't think that z is evaluated first in fact I would expect that your second example would have an answer 6. Unless I'm reading the spec wrong I think the unary and prefix operator have the same precedence so they are evaluated left-to-right.
Wes - Anonymous
April 20, 2004
Definately 20 and 16.
This isnt all that hard.
First:
int z = 2;
z += ++z+5z++;
unary-expressions first from left to right
z = 2 + (3) + (5 * 3);
Then the mulitplication
z = 2 + 3 + 15
Then Addition
z = 20
Second one:
int z = 2;
z += (5z++)+(++z);
brackets first
z = 2 + ((5*2)++) + (++z);
then unary
z = 2 + 10++ + (3);
then addition
z = 2 + 11 + 3;
z = 16
Simple no? - Anonymous
April 20, 2004
Wes,
Is the occurance of z without any operators applied (such as x = z) considered having a unary operator applied? I guess my question is where to identifiers without any unary operators fall in the operator precedence and associativity table? I intrepreted the spec to mean that an identifier (i.e. simple-name, i.e. primary-no-creation-expression, i.e. primary-expression) came first under the "Primary" category in the operator precedence and associativity table:
primary-expression: // "Primary" Category
primary-no-array-creation-expression
array-creation-expression
primary-no-array-creation-expression:
literal
simple-name
parenthesized-expression
member-access
invocation-expression
element-access
this-access
base-access
post-increment-expression
post-decrement-expression
object-creation-expression
delegate-creation-expression
typeof-expression
checked-expression
unchecked-expression
simple-name:
identifier // z
unary-expression: // "Unary" Category
primary-expression
+ unary-expression
- unary-expression
! unary-expression
~ unary-expression
pre-increment-expression // ++z
pre-decrement-expression
cast-expression
Here's a link to the docs I'm looking at:
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003JUL.1033/csspec/html/vclrfcsharpspec_7_2_1.htm
-Kael - Anonymous
April 20, 2004
Following a left-to-right derivation of the grammar rules I would assume that ++z is evaluated first:
expression
conditional-expression
conditional-or-expression
conditional-and-expression
inclusive-or-expression
exclusive-or-expression
and-expression
equality-expression
relational-expression
shift-expression
additive-expression
additive-expression + multiplicative-expression
multiplicative-expression + multiplicative-expression
unary-expression + multiplicative-expression
pre-increment-expression + multiplicative-expression
++unary-expression + multiplicative-expression
++primary-expression + multiplicative-expression
++simple-name + multiplicative-expression
++identifier + multiplicative-expression
++z + multiplicative-expression
++z + unary-expression
++z + primary-expression
++z + simple-name
++z + identifier
++z + z
I don't think the precedence matters in this case because there is no conflict, meaning that the '+' in the middle is of lower precedence than both so you need to evaluate the arguments for the '+' from left-to-right.
If I'm wrong here please someone correct me.
Wes - Anonymous
April 20, 2004
Just for kicks, compile the code and open the assembly with Anakrino. The output is nowhere near being correct. :-) - Anonymous
April 20, 2004
The comment has been removed - Anonymous
April 20, 2004
Sean,
I took your advice and opened it with Lutz's .NET Reflector and it's nowhere near correct either:
int num1;
num1 = 2;
num1 = (num1 + 1);
num1 = (num1 + 1);
num1 = (num1 + ((num1 + 1) + (5 * num1)));
Console.WriteLine("Yak! {0}", num1);
Hmm, have we discovered some new form of obfuscation? (As if the original code isn't obfuscated enough already)
-Kael - Anonymous
April 20, 2004
The comment has been removed - Anonymous
April 20, 2004
Kael,
With your particular example x + y * z, y has two possible operators to be used with thus there is a conflict and * has higher precedence so it used.
What typically happens with these type expressions is a parse tree is created using the grammar rules and then that tree is evaluated using a pre-order traversal. I would draw out your parse tree but it is kind of hard to do in the comments :)
Essentially what is happening for your expression is that you are going to add x to the result of the multiplicative-expression which is y * z. Where as with the ++z + z you are going to add the result of the pre-increment-expression to the result of the multiplicative-expression.
I think if you look at the parse trees for these expressions it will help your understanding.
Wes - Anonymous
April 21, 2004
The comment has been removed - Anonymous
December 27, 2004
[http://itpeixun.51.net/][http://aissl.51.net/][http://kukuxz003.freewebpage.org/][http://kukuxz001.51.net/][http://kukuxz003.51.net/][http://kukuxz005.51.net/][http://kukuxz002.51.net/][http://kukuxz004.freewebpage.org/][http://kukuxz007.51.net/][http://kukuxz001.freewebpage.org/][http://kukuxz006.51.net/][http://kukuxz002.freewebpage.org/][http://kukuxz004.51.net/][http://kukuxz008.51.net/][http://kukuxz009.51.net/][http://kukuxz005.freewebpage.org/][http://kukuxz006.freewebpage.org/][http://kukuxz007.freewebpage.org/][http://kukuxz009.freewebpage.org/] - Anonymous
June 07, 2009
PingBack from http://weakbladder.info/story.php?id=5791