30

Can someone tell me how prefix / postfix operators really work? I've been looking online a lot but haven't found anything.

From what I can tell prefex first increments, then does the operation and then assigns.
Postfix would do the operation first, then assign and then increment.

But I'm having a bit of trouble with my code:

int x, y;
x = 1;
y = x + x++; // (After operation y = 2)(x=2)

However when I do:

y = x++ + x; // (After operation y = 3)(x=2)

I'm not sure why these operations would be any different. I have two questions:

  • Could you explain the difference?

  • How does this apply to the other operator Prefix?

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
GodsCrimeScene
  • 1,260
  • 2
  • 17
  • 35
  • 13
    Which language are you really interested in? C# and C can work very differently here... – Jon Skeet Oct 18 '11 at 18:55
  • 1
    `x++ + x` is underfined behavior... –  Oct 18 '11 at 18:58
  • JavaScript also exhibits the same behavior. I have a feeling in his particular example that the `++` operator is well enough defined that it will return the same result in C, C++, C#, Java and JavaScript. Just a hunch though. – Matt Greer Oct 18 '11 at 18:58
  • 2
    @VladLazarenko no it's not, since you only modify x once. – Luchian Grigore Oct 18 '11 at 18:59
  • 3
    @LuchianGrigore: It is indeed an UB and because order of evaluation of arguments here is not defined. – Alok Save Oct 18 '11 at 19:00
  • @JonSkeet C#, didn't know the c languages handled it differently. – GodsCrimeScene Oct 18 '11 at 19:01
  • 2
    @timmy: C# makes more guarantees about the order in which operations occur. – Jon Skeet Oct 18 '11 at 19:02
  • @JonSkeet: Would this apply both to the compiler and the runtime as well, given any JIT optimizations? I could easily see an "impedance mismatch" between mono/.NET implementations when the code is compiled on one and executed on the other, especially when applying various naïve optimizations. – Art Taylor Oct 18 '11 at 19:29
  • @ArtTaylor: It's all down to the compiler. And it's well specified in the C# language spec - I can't imagine the IL ending up with any ambiguity. – Jon Skeet Oct 18 '11 at 19:36
  • 4
    Short advice: don't use them, unless as shortcut for x = x+1. Take code readability in mind. – Pablo Venturino Oct 18 '11 at 20:10
  • @PabloVenturino: Especially if you are unsure if you could be operating on a subclass with an overloaded ++ operator with some (extra) side effects. – Art Taylor Oct 18 '11 at 20:20
  • Per @timmy's comment I've removed the C++ and C tags. – user7116 Oct 19 '11 at 01:57
  • @sixlettervariables: If you refer `didn't know the c languages handled it differently` as the comment,then it does not call for removal of the c/c++ flags.All the answers here distinguish the behavior for c,c++ & c#.So all the tags are relevant Also,that is what the OP tagged the original Q with,removing c,c++ tag is a loss of this nicely put information.Re-Adding the tags. – Alok Save Oct 19 '11 at 03:46
  • possible duplicate of [Sequence points and partial order](http://stackoverflow.com/questions/1895922/sequence-points-and-partial-order) – Ben Voigt Oct 19 '11 at 03:55

7 Answers7

62

This question gets asked a fair amount. Be warned that every time someone asks this question a great many people post incorrect answers. Lots of people have incorrect ideas about how these operators work, including people who write programming books and thereby teach other people falsehoods. Read the other answers here very carefully.

For a correct analysis of the C# behaviour, see:

What is the difference between i++ and ++i?

For C++ any behaviour is correct behaviour, in those cases where you are observing a side effect. C++ does not define when the side effect of the increment is visible. Any two compilers can do it differently.

A good rule to follow is to not rely on the order in which side effects happen in any language, but certainly do not rely on it in C++, because it is not reliable.

To look at your specific case:

int x, y;     
x = 1;     
y = x + x++; 

You report that x and y are both 2. That is correct in C#. In C# the correct behaviour is:

  • evaluate y as a variable
  • evaluate x as a value -- it is 1
  • evaluate x++ as a value. This evaluates x as a variable, then takes its original value which is 1, then increments that value, which is 2, then assigns 2 to x and then results in the original value, which is 1.
  • evaluate 1 + 1, which is 2
  • assign 2 to y.

So x and y are both 2 in C#.

C++ can do the same thing, but it is permitted to evaluate the addition in right-to-left order. That is, it is permitted to do:

  • evaluate x++ as a value. This evaluates x as a variable, then takes its original value which is 1, then increments that value, which is 2, then assigns 2 to x and then results in the original value, which is 1.
  • evaluate x as a value -- it is 2
  • evaluate 1 + 2, which is 3
  • evaluate y as a variable
  • assign 3 to y.

C++ is also permitted to do this:

  • evaluate x++ as a value. This evaluates x as a variable, then takes its original value which is 1, then increments that value, which is 2 ... step missing here ... and then results in the original value, which is 1.
  • evaluate x as a value -- it is 1
  • evaluate 1 + 1, which is 2
  • assigns 2 to x -- the step that was missing before.
  • evaluate y as a variable
  • assign 2 to y.

So in C++, you can get y as 3 or 2, depending on the whim of the compiler writer. In C# you always get that y is 2. In C++, the increment's assignment can happen at any time, as long as it does happen. In C#, the increment's assignment must happen after the incremented value is computed and before the original value is used. (When observed from the executing thread; if you are trying to observe this stuff from another thread or threads, all bets are off.)

In your second example:

y = x++ + x; 

In C# the required behaviour is:

  • evaluate y as a variable
  • evaluate x++ as a value. This evaluates x as a variable, then takes its original value which is 1, then increments that value, which is 2, then assigns 2 to x and then results in the original value, which is 1.
  • evaluate x as a value -- it is 2
  • evaluate 1 + 2, which is 3
  • assign 3 to y.

So the correct answer in C# is that y is 3 and x is 2.

Again, C++ can do these steps in any order. C++ is permitted to do:

  • evaluate x as a value -- it is 1
  • evaluate x++ as a value. This evaluates x as a variable, then takes its original value which is 1, then increments that value, which is 2, then assigns 2 to x and then results in the original value, which is 1.
  • evaluate 1 + 1, which is 2
  • evaluate y as a variable
  • assign 2 to y.

Again, in C++ the correct answer is that y is 2 or 3, depending on the whim of the compiler writer. In C# the correct answer is that y is 3.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 18
    In summary: if you have `x++` or `++x`, then `x` better not be anywhere else on that line. – Mooing Duck Oct 18 '11 at 19:03
  • 1
    @MooingDuck: That is a good rule of thumb. The actual rule in C++ is to know what the rules are for "sequence points" and follow them. (Look it up on Wikipedia if you don't know what a "sequence point" is in C++.) – Eric Lippert Oct 18 '11 at 19:12
  • @EricLippert: I thought about mentioning the comma, but then realized that commas are confusing because they're only _sometimes_ sequence points. I stuck with the simple (but inaccurate) version. – Mooing Duck Oct 18 '11 at 19:34
  • @EricLippert Eric, in reading your post you linked i'm confused how memory differs between the two? In prefix it sounds like it goes->create temp int memory area->do math->assign back to original->get rid of temp memory area? And postfix->create temp memory area->do math->assign back to orignal->keep temp around for later same variable operations? – GodsCrimeScene Oct 18 '11 at 20:00
  • 1
    @timmy: Good question! The short answer is that the C# compiler generates a variety of different MSIL opcodes depending on the specific situation and whether or not optimizations are turned on. But IL does not actually run of course. The jitter turns those opcodes into machine-specific instructions. How the jitter chooses to allocate temporary memory is up to it; odds are good that it enregisters the temp. For the exact details, either (1) write a bunch of C# programs that do different kinds of ++ and see what ILDASM and the debugger tell you, or (2) open up a new question. – Eric Lippert Oct 18 '11 at 21:19
  • 1
    @timmy: and of course if the jitter can prove that doing so is safe, it need never allocate any temporary. It could simply enregister the variable and update it to the correct value in place, if it knows that the value and the side effect are not observed. Obviously that is the common situation; if you have `for(int i = 0; i < count; ++i)` then it does not matter one bit what the value of the expression is; the only thing that is observable is the side effect, so the temp can go away entirely. – Eric Lippert Oct 18 '11 at 21:22
  • I came here because I confused with how I could simple read this ++x or x++. So x++ means give me an x and ++ it, ++x means ++ it and give me that x. – Ariwibawa Sep 19 '20 at 06:01
29
  • In C# the operands of + are evaulated in left-to-right order.
  • In C and C++ the order of evaluation for the operands of + is unspecifed.

For C# your examples work as follows:

 y = x + x++;
     ^ x is 1
         ^ x is increased to 2, but the postfix increment returns the old value (1)
 y = 2

 y = x++ + x;
     ^ x becomes 2, but postfix increment returns the old value (1)
           ^ x is now 2 here
 y = 3
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 2
    This doesn't really address his question though, in both examples he is using postfix. I don't have a C or C# environment handy, so I am curious what others have to say. – Matt Greer Oct 18 '11 at 18:56
  • @MarkByers Thanks Mark, this is a very good visual of what is happening. – GodsCrimeScene Oct 18 '11 at 19:25
  • 1
    @Matt Greer Actually it does - x is 2 _after "x++" returns_, not after the whole statement. This is only defined in C# among the languages mentioned. In C/C++, it's undefined, and x could be zero, or infinity, or anything in between, or your computer could explode. – Random832 Oct 18 '11 at 21:26
  • 2
    Take careful note of Mark's phrasing. As Mark indicates, (in C#) `x++` means "increment `x`, then return the old value of `x`. `x++` does **not** mean, "return the value of `x`, then increment `x`. – Brian Oct 19 '11 at 16:41
7

In C and C++:
The output is Unspecified.

Reference - C++03 Standard:

Section 5: Expressions, Para 4:

except where noted [e.g. special rules for && and ||], the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is Unspecified.

In C99 Section 6.5.

"The grouping of operators and operands is indicated by the syntax.72) Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified."

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Unspecified is not Undefined behavior, is it? I though unspecified meant Implementation Defined. (I could be wrong) – Mooing Duck Oct 18 '11 at 19:05
  • @MooingDuck: No it is not, Check [undefined,unspecified and implementation defined behavior](http://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – Alok Save Oct 18 '11 at 19:07
  • 1
    No, no: the behavior is not unspecified, it is _undefined_. You forgot the rest of §5/4: "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored." The OP's code violates the second sentence. – James McNellis Nov 06 '11 at 17:37
6

In both cases, the increment was applied after the x was used. In the first, it was evaluated as follows: y = 1 + 1 (incremented to 2)

in the second

y = 1 (incremented to 2) + 2.

That's why you got different answers.

Lou
  • 1,955
  • 14
  • 16
4

The expressions x++ and ++x have both a result (or value) and a side effect.

If we restrict our discussion to integral type operands, the result of x++ is whatever the current value of x is. The side effect is to increment x by 1. Thus, given the code

x = 0;
y = x++;

the result will be x == 1 and y == 0 (assuming x and y are integral types).

For ++x, the result is 1 plus the current value of x. The side effect is to increment x by 1. Thus, given the code

x = 0;
y = ++x;

the result will be x == y == 1.

What distinguishes C and C++ from C# is when operands are evaluated and when the side effects are applied. C# guarantees that operands in an expression are always evaluated left-to-right. C and C++ only guarantee left-to-right evaluation for the &&, ||, ?:, comma, and function-call () operators - for all other operators, the order in which operands are evaluated is unspecified.

Similarly, in C#, the side effects of x++ and ++x will be applied immediately after the expression has been evaluated, whereas C and C++ only require that the side effect be applied before the next sequence point.

C#'s rules on evaluation guarantee that the expressions like x = x++, a = b++ * b++, and a[i] = i++ are well-defined, whereas the C and C++ language definitions explicitly say such expressions result in undefined behavior (any result is possible).

John Bode
  • 119,563
  • 19
  • 122
  • 198
3

x + x++ and x++ + x are an example of pathological side-effects cases you don't want to depend on. x++ and ++x both increment x, but in adding x the order of evaluation is undefined - the compiler can choose which 'side' it evaluates first.

n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • It's not undefined. In both cases, you only modify x once and the order of operation is completely defined by the standard. – Luchian Grigore Oct 18 '11 at 19:00
  • @n8wrl Not sure I agree with that. Parenthesis always helps. – Lou Oct 18 '11 at 19:00
  • Parenthesis do help, but that doesn't change the fact. – Luchian Grigore Oct 18 '11 at 19:03
  • 2
    @LuchianGrigore: The order in which the *operators* execute is defined. **The order in which the *operands* are evaluated and side effects become visible is not defined in C++.** In the cases given by the original poster, the behaviour is in fact *unspecified* by the C++ specification. – Eric Lippert Oct 18 '11 at 19:17
0

Consider:

y = x + x++;

Whether its behavior is defined or not (it's undefined in C and C++; apparently it's well defined in C#), whatever it is you're trying to do, there's bound to be a better way to express it.

If you're assuming strict left-to-right evaluation, then the above could be written as:

y = x * 2;
x ++;

The meaning is clear and unambiguous to any reader who knows what =, *, and ++ mean, and future maintainers of your code won't be tempted to hunt you down.

Or you could write x + x or x << 1 if you don't trust the compiler to generate efficient code, but such mistrust is usually misplaced.

If you insist, you could even write:

y = x++ * 2;

That's a little terse for my personal tastes, but it's still unambiguous.

If you want to understand somebody else's code (which admittedly is something programmers spend a great deal of time doing), then understanding convoluted expressions can be important. But when you're writing your own code, clarity is more important than saving keystrokes (or showing off how well you know the operator precedence charts).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631