8

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,

nvoigt
  • 75,013
  • 26
  • 93
  • 142
as9876
  • 934
  • 1
  • 13
  • 38
  • 4
    Assignments can be either expressions or statements; Consider `var a = 1; var b = (a = 2);`. (More info from Eric Lippert can be found [here](http://stackoverflow.com/a/3807583/298053)). – Brad Christie Oct 12 '15 at 00:18
  • 1
    The fundamental misunderstanding is the belief that `i++` and `++i` are exactly equivalent *syntactically* to anything at all. If it bothers you to think of them as sugars, you don't have to. **They are expressions in their own right, and they have very carefully defined meanings.** Ignore the accepted answer in the question you linked to; read my answer instead and see if it helps. – Eric Lippert Jan 21 '18 at 15:49
  • Isn't `++i` syntactically equivalent to `(i += 1)`? – DodgyCodeException Jan 21 '18 at 17:03
  • @DodgyCodeException: First, equivalence should logically flow both ways, but I presume that you mean that the equivalence flows only one way. So: is it the case that *every* usage of `++i` can be turned into `(i += 1)` and the program remains a legal program with the same output? Give that some thought. Can you find a counterexample? – Eric Lippert Jan 21 '18 at 17:14
  • @EricLippert the only thing I can think of is a user-defined operator, which doesn't apply when `i` is a plain int. – DodgyCodeException Jan 21 '18 at 19:21
  • 1
    @DodgyCodeException: User-defined operators are a good example, and *you cannot know if you have a user-defined operator or not without doing semantic analysis*, which means that it's not a syntactic property. – Eric Lippert Jan 21 '18 at 19:53
  • 1
    @DodgyCodeException: We might also think about cases like `++q->i` not being the same as `(q+=1)->i`, but in those cases it is somewhat abusive to think of `++q` as a thing at all, since it is not actually a sub-expression in that program fragment. That *would* be something we could identify syntactically. – Eric Lippert Jan 21 '18 at 19:57
  • 1
    @DodgyCodeException: We can also easily see that the equivalence does not run in the other direction. `s += 1` for string s is perfectly legal but `++s` is not. – Eric Lippert Jan 21 '18 at 20:45

3 Answers3

20

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    To start, I think this is a high-quality answer, and have up-voted it. But with respect to the first line; I think one of the great things about SO is that it's a knowledge resource that is constantly evolving. I think it's good to have people coming back and checking in on old questions to make sure they're adequately answered (this answer is a great example of that), and I wouldn't want to make people feel like they shouldn't _try_ to do that (when really we could use more of it), even if erroneous (the up and down votes, alongside comments, will settle what is good from bad). – Ironcache Jan 22 '18 at 11:50
  • What about `i += 1`? Is it equivalent to `i = i + 1`? – Luca Cremonesi Jan 22 '18 at 15:42
  • @LucaCremonesi: If `i` is an integer variable, there are no exceptions, and so on, as noted above, then yes. If you want to know the rules for, say, what if `i` is short or there is a side effect or something like that, consult the specification. – Eric Lippert Jan 22 '18 at 15:43
  • @LucaCremonesi: Pop quiz: suppose `i`, `m` and `n` are `double`. Is `i += m + n` guaranteed to be the same as `i = i + m + n`? – Eric Lippert Jan 22 '18 at 15:47
  • Is it `i += m + n` equivalent to `i = i + (m + n)` (with `i` evaluated only once) while `i = i + m + n` is equivalent to `i = (i + m) + n`? If so, the two assignments can be different in general due to the double precision floating point arithmetic. – Luca Cremonesi Jan 22 '18 at 16:38
  • You got it in one! There are some particularly nasty possibilities, such as, i + (m + n) is Infinity but (i + m) + n is finite, say, because m and n are both very large and i is negative. – Eric Lippert Jan 22 '18 at 16:43
13

Edit: See Eric's answer, mine is meh.


You are right. The ++ is equivalent to i = i + 1, but the point to remember is that i = i + 1 is not only a statement, but can also be used as an expression:

Console.WriteLine((i = i + 1) * 42); // will inc i, and then print i*42
Console.WriteLine(++i * 42); // exactly same

Console.WriteLine(i++ * 42); // will inc i, and print old_i*42
Console.WriteLine((i++) * 42); // exactly same

So to conclude simply:

// this
int j = ++i * 42;
// behaves like
int j = (i = i + 1) * 42;

// but this
int j = i++ * 42;
// actually behaves like
int prev = i;
i = i + 1;
int j = prev * 42;
SimpleVar
  • 14,044
  • 4
  • 38
  • 60
  • When i starts with 2, result: 126, 168, 168, 210 – as9876 Oct 12 '15 at 00:19
  • Not meant to be executed line by line, but just presenting the different ways and their logic to show how they relate to the `i = i + 1` – SimpleVar Oct 12 '15 at 00:20
  • Someone must have come from hitchhikersguidetothegalaxy.stackexchange.com – Brad Christie Oct 12 '15 at 00:21
  • @BradChristie Or I just smoked until I dropped the zero. – SimpleVar Oct 12 '15 at 00:21
  • 1
    Though this answer gets the idea across for simple usages of `i++`, it is wrong for more complex usages. Consider for example `class C { int i; int M() { return i; } int N() { int j = i++ * 42 + M(); return j); }}` That is NOT equivalent to `int j = i * 42 + M(); i = i + 1; return j;` It is equivalent to `int temp = i; i = i + 1; int j = temp * 42 + M(); return j;` which produces a different result. **In C# the side effect happens before the value is produced, not after**, so your desugaring should reflect that. – Eric Lippert Jan 21 '18 at 15:54
0

I think you may consider the following two functions to get an analogy to the i++ and ++i.

Fiddle

public static int IPlusPlus(ref int i) {
    // simply increment i before returning it
    i = i + 1;
    return i;
}

public static int PlusPlusI(ref int i) {
    // increment i only after you already returned it
    try {
        return i;
    }
    finally {
        i = i + 1;
    }
}

Just like function calls, i++ and ++i are right-hand-side expressions, meaning var a = i++; and var a = ++i; are valid statements. But i++ = 5; and ++i = 5; are not valid as that would use them as left-hand-side expressions.

nl-x
  • 11,762
  • 7
  • 33
  • 61
  • Though this is a pretty reasonable way to get the idea across, I note there are a couple of small problems. First, we don't really need all the mechanisms of try-finally; `int temp = i; i = i + 1; return temp; ` is easier to follow. Second, C# 7 now allows you to put a function call on the left side of an assignment if the function is ref-returning. – Eric Lippert Jan 21 '18 at 17:19
  • @EricLippert First. I considered using the temp-variant as well, but thought a try-finally would be more clear. Second. Thanks, I wan't aware of (the LHS assignment to) ref-returning functions, which sound cool. – nl-x Jan 21 '18 at 17:21
  • The rule in C# is that to be on the left side of an assignment, an expression has to be classified as a variable (or a property with a setter, or an indexer with a setter, and so on.) Normally a function returns a value, not a variable, but ref-returning function calls are classified as variables. – Eric Lippert Jan 21 '18 at 17:23