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?

grg
  • 5,023
  • 3
  • 34
  • 50
Aremyst
  • 1,480
  • 2
  • 19
  • 33
  • 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. – shahkalpesh Apr 21 '17 at 10:11
  • 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 1 – Irwene Apr 21 '17 at 10:11
  • It is because of post increment operator. Try reading this excellent answer from Eric Lippert to understand on how post increment works: http://stackoverflow.com/questions/3346450/what-is-the-difference-between-i-and-i/3346729#3346729 – Boney Apr 21 '17 at 10:13
  • Yes, but the post increment operator IS modifying `i` which is a `ref`while 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 0 – Irwene Apr 21 '17 at 10:15
  • 33
    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 practice – Alexander Apr 21 '17 at 13:41
  • 2
    @Alexander agreed. If someone working for me wrote code like this, I'd sit down and have a "talk" with them – user2023861 Apr 21 '17 at 13:50
  • Typo, my comment should have been "Although this is an awesome* puzzle ..." – Alexander Apr 21 '17 at 14:53
  • 1
    Somewhat related question that also gives some good info on what happens behind the scenes: https://stackoverflow.com/questions/33783989/post-increment-within-a-self-assignment – Siyual Apr 21 '17 at 15:53
  • 3
    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?" – svick Apr 21 '17 at 18:01
  • The marked duplicate isn't a duplicate at all. Vote to reopen. – Ofek Shilon Apr 01 '20 at 07:56

3 Answers3

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.

Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39
  • 3
    ... you were a little faster than me ;) – DarkSquirrel42 Apr 21 '17 at 10:17
  • Thank you, Jakub, for the details, it makes sense now why it was 1 and became 0 again. – Aremyst Apr 21 '17 at 10:19
  • 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 guess – user1935361 Apr 21 '17 at 12:15
  • 8
    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. – Marc van Leeuwen Apr 21 '17 at 12:18
  • @user1935361 Yes, that would be `i = 0 + 1` – Jakub Lortz Apr 21 '17 at 12:18
  • @MarcvanLeeuwen In C++ that would be undefined behavior, right? – Jakub Lortz Apr 21 '17 at 12:36
  • 4
    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 http://stackoverflow.com/q/24194076/1436796. – Marc van Leeuwen Apr 21 '17 at 13:00
  • 4
    @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. – Justin Time - Reinstate Monica Apr 21 '17 at 13:58
  • 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? – alpham8 Apr 27 '17 at 10:15
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

DarkSquirrel42
  • 10,167
  • 3
  • 20
  • 31
  • 3
    nothing to do with precedence, everything to do with left-to-right evaluation. – Pete Kirkham Apr 21 '17 at 14:05
  • read carefully ... operator precedence != operATION precedence ... which results in left to right order ... – DarkSquirrel42 Apr 21 '17 at 14:06
  • 4
    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. – Pete Kirkham Apr 21 '17 at 14:08
  • well... thanks for that lesson in english vocabulary... – DarkSquirrel42 Apr 21 '17 at 14:15
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.

Debsdoon
  • 51
  • 7
  • 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: http://stackoverflow.com/questions/3346450/what-is-the-difference-between-i-and-i/3346729#3346729 – Tanner Swett Apr 21 '17 at 12:00