Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What Does i++ Really Mean?

Tags:

c#

The question has been asked many times, "What's the difference between i++ and ++i". The accepted answer at What is the difference between i++ and ++i?, and I've seen this language in many other places as well, is that, "i++ means 'tell me the value of i, then increment', whereas ++i means 'increment i, then tell me the value'.

What confuses me is that I was not aware that we were discussing getting back a value for i in either scenario. I thought that i++ is syntactically equivalent to:

i = i + 1;

which is a statement, not an expression, so I don't understand where i is being returned at all.

Can you please expain what the statement actually means?

Thanks,

like image 457
as9876 Avatar asked Oct 12 '15 00:10

as9876


1 Answers

For reasons unknown to me, this old question with an accepted answer is attracting new answers today, many of which contain significant errors or omissions. Let me attempt to answer the question as asked definitively.

The question has been asked many times, "What's the difference between i++ and ++i". The accepted answer at [...], and I've seen this language in many other places as well, is that, "i++ means 'tell me the value of i, then increment', whereas ++i means 'increment i, then tell me the value'.

As I noted in my answer to that same question: this characterization is common and a reasonable first cut at understanding but unfortunately misleading when you look at the semantics more carefully. Please do not be misled by this vague and not entirely accurate characterization. Read my answer to that question instead.

What confuses me is that I was not aware that we were discussing getting back a value for i in either scenario. I thought that i++ is syntactically equivalent to i = i + 1; which is a statement, not an expression, so I don't understand where i is being returned at all.

You have a number of misunderstandings here. Rather than attack them all, let's just say what the truth is.

First, ++i and i++ are not syntactically exactly equivalent to anything. You cannot necessarily take a legal program that contains an ++i or an i++ and transform it solely syntactically into another legal program. So just banish that thought from your head. These are morally equivalent to increment-and-assign, and ought to be semantically equivalent, but there is not necessarily a syntactic desugaring that preserves program semantics or legality.

Let us now say some more true things. But first, some caveats. For the purposes of this discussion, the incremented expression i is a variable of type int, which can be evaluated either as a variable or a value without side effects, including exceptions. Moreover, we suppose that the incrementing operation does not produce an exception. And moreover we presume a single thread of execution. If you wish to know the semantics of increments and assignments in cases where evaluating the variable can throw, or produce another side effect, or is not a variable but rather is a property, or the operations are user-defined, or multiple threads are observing or mutating the variable, see the specification for details.

That said, here are some true facts:

  • ++i, i++, and i = i + 1 are expressions
  • ++i;, i++; and i = i + 1; are statements
  • The semantics of ++i are as follows:

    1. temp1 is given the value of i
    2. temp2 is given the value of temp1 + 1
    3. i is given the value of temp2
    4. the value of the expression is temp2
  • The semantics of i++ are as follows:

    1. temp1 is given the value of i
    2. temp2 is given the value of temp1 + 1
    3. i is given the value of temp2
    4. the value of the expression is temp1
  • Notice that the difference between the two forms is only what the value produced is. The steps that are taken to produce the side effect are identical in both cases. You are guaranteed in single-threaded C# that the side effect is observed complete before the value is produced.

  • The semantics of i = i + 1 are as follows:
    1. temp1 is given the value of i
    2. temp2 is given the value of temp1 + 1
    3. i is given the value of temp2
    4. the value of the expression is temp2
  • Notice that the semantics of i = i + 1 are identical to the semantics of ++i. This is not a guarantee that you can syntactically substitute i = i + 1 for ++i or vice versa, in an arbitrary program. In certain programs, this might be possible.
  • Notice that the semantics of i++ does not admit an "easy" semantically equivalent form. ((Func<int, int, int>)((int j, int k)=>j))(i, i=i+1) has the same semantics but is obviously a crazy thing to type.

  • The semantics of the statement forms of all three are:

    1. Evaluate the expression as normal.
    2. Discard the result.

Hopefully this definitively clears up any misunderstandings about what is an expression, what is a statement, what side effects and values are produced by the expressions, and in what order do they happen. Again, note that this explanation is narrowly targeted at simple cases involving integer variables without side effects on a single thread. For the details of how these operations work on other types, or interact with exceptions, or how they work in multithreaded programs, consult the C# specification or ask a more specific question.

Finally: I personally still find all this confusing, and I've been programming in C descendant languages for 30 years and I implemented these semantics in C#. If I find them confusing, and if almost every answer to every question I see on these operators contains significant errors or omissions, then we can safely conclude that these are confusing operators. As a result, I almost never use ++ or -- in my production code. I think it is bad style to have an expression that is useful for both its value and its side effects.

Try to find a way to structure your program so that ever statement has one side effect, and side-effecting expressions are limited to expression statements. Avoid ++, and particularly avoid any scenario where i++ and ++i would have different semantics, because that's a point where the program is going to be harder to understand and therefore harder to maintain correctly.

like image 68
Eric Lippert Avatar answered Sep 25 '22 07:09

Eric Lippert