Share via


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 += (5
    z++)+(++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