Interesting interview exercise result: return, post increment and ref behavior [duplicate]
Asked Answered
T

3

60

Here's a simple console application code, which returns a result I do not understand completely.

Try to think whether it outputs 0, 1 or 2 in console:

using System;

namespace ConsoleApplication
{
    class Program
    {
        static void Main()
        {
            int i = 0;
            i += Increment(ref i);

            Console.WriteLine(i);
            Console.ReadLine();
        }

        static private int Increment(ref int i)
        {
            return i++;
        }
    }
}

The answer is 0.

What I don't understand is why post increment i++, from the Increment method, which is executed on a ref (not on a copy of the passed variable) does increment the variable, but it just gets ignored later.

What I mean is in this video:

Can somebody explain this example and why during debug I see that value is incremented to 1, but then it goes back to 0?

Tasso answered 21/4, 2017 at 10:2 Comment(10)
When doing return i++, the value of i (which is 0) returned before it gets incremented. While the increment may happen, the value is discarded because it is returned already. Try doing return ++i; instead.Numerology
Yes, but you're updating the ref.... which should not get back down to 0. And it is executed, because the value in the debugger is shown to be updated to 1Philbrick
It is because of post increment operator. Try reading this excellent answer from Eric Lippert to understand on how post increment works: #3346950Djokjakarta
Yes, but the post increment operator IS modifying i which is a refwhile I do know why the value 0 would be returned in a 'normal' case, I don't know why it would get up to 1 then back down to 0Philbrick
Although this is answer puzzle, it's a pretty dumb interview question, IMO. It should be tagged "language-lawyer", because this behaviour is contingent on a very niche language rule that you would hopefully never observe in action, because (hopefully) you'll never see code like this in practicePsychosocial
@Psychosocial agreed. If someone working for me wrote code like this, I'd sit down and have a "talk" with themPetrosal
Typo, my comment should have been "Although this is an awesome* puzzle ..."Psychosocial
Somewhat related question that also gives some good info on what happens behind the scenes: #33784489Cabstand
If I got this code in an interview, my first answer would be: "Can I write tests for this code (if they don't already exist) and then refactor it to be sane?"Rowley
The marked duplicate isn't a duplicate at all. Vote to reopen.Zermatt
F
102

i += Increment(ref i); is equivalent to

i = i + Increment(ref i);

The expression on the right hand side of the assignment is evaluated from left to right, so the next step is

i = 0 + Increment(ref i);

return i++ returns the current value of i (which is 0), then increments i

i = 0 + 0;

Before the assignment the value of i is 1 (incremented in the Increment method), but the assignment makes it 0 again.

Fadiman answered 21/4, 2017 at 10:15 Comment(9)
... you were a little faster than me ;)Latea
Thank you, Jakub, for the details, it makes sense now why it was 1 and became 0 again.Tasso
So if that line was i = Increment(ref i) + i the result would be different? I'm not gonna lie, this is the first time the 'left to right' rule has ever had an effect on my mental model...outside of boolean short circuiting I guessCockloft
Disclaimer: I have a C++ background, know little about C#. But I'm baffled at this. So apparently the C# definition forces the compiler, in a statement like i+=f();, to start making a local anonymous copy of the value of i, then call f which can (if it has access) fool around with the variable i all it wants, knowing that the value left in that variable will be forgotten anyway because it will be overwritten by the sum of the anonymous copy made earlier of i and the value returned by f. Very curious. Seems to go against the idea of making += simple and fast.Prank
@Cockloft Yes, that would be i = 0 + 1Fadiman
@MarcvanLeeuwen In C++ that would be undefined behavior, right?Fadiman
No, off the top of my head I think it would be well defined. While the C++ standard does basically (and lazily) define var+=expr as equivalent to var=var+expr it does make 2 exceptions: the lvalue var is evaluated only once, and "with respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation". That is not entirely clear, but it does seem to forbid implementing the operation using a fetch of var before, and a store of var after the function call. In case you care, see https://mcmap.net/q/330424/-in-c-11-does-i-i-1-exhibit-undefined-behavior/1436796.Prank
@MarcvanLeeuwen It makes sense if evaluation order is defined as strictly being from left to right. Given var = var + expr, and left-to-right evaluation, var will always be checked before expr is evaluated, so any changes expr makes to var won't be reflected in it. I guess C# just defines stricter sequencing for it than C++, or something.Vice
Sure, that Increment returns 1? I think there is also an prefix/suffix addition problem with it. return i++ should strictly seen return 0, because: First return and then add 1 to the value. So, I think suffix add doesnt make sense in that case, does it?Hadsall
L
18

i think the "magic" here is just operation precedence the order of operations

i += Increment(ref i)

is the same as

i = i + Increment(ref i)

the + operation is executed left to right

so first we take i ... wich is 0 at that time ...

then we add the result of Increment(ref i) ... which is also 0 ... 0+0=0 ... but wait ... before we get that result i is actually incremented ...

that increment takes place after the left operand of our + operation has ben evaluated ... so it does not change a thing ... 0+0 still is 0 ... thus i is assigned 0 after the + operation has been executed

Latea answered 21/4, 2017 at 10:16 Comment(4)
nothing to do with precedence, everything to do with left-to-right evaluation.Lizbeth
read carefully ... operator precedence != operATION precedence ... which results in left to right order ...Latea
I read carefully. You wrote badly. Use the correct term - order of operations, not precedence. Precedence determines how the AST is constructed from otherwise ambiguous input. Order of operations/evaluation determines how the AST is evaluated to a result. Using the phrase 'operation precedence' means neither.Lizbeth
well... thanks for that lesson in english vocabulary...Latea
T
0

As you mentioned - postincrement "i++". statement - "return i++;" will set the value of 'i' in memory after original value is returned.

try using "return ++i;" and probably you will get it.

Takakotakakura answered 21/4, 2017 at 10:18 Comment(1)
I don't think this is correct. The statement return i++; increments i, and then, after that, returns the previous value of i, which is 0. I'm getting that information from Eric Lippert's answer here: #3346950Admittance

© 2022 - 2024 — McMap. All rights reserved.