By GodsCrimeScene


2011-10-18 18:53:10 8 Comments

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?

7 comments

@Eric Lippert 2011-10-18 18:58:59

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.

@Mooing Duck 2011-10-18 19:03:11

In summary: if you have x++ or ++x, then x better not be anywhere else on that line.

@Eric Lippert 2011-10-18 19:12:26

@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++.)

@GodsCrimeScene 2011-10-18 19:30:49

@EricLippert Thanks for the detailed explanation!

@Mooing Duck 2011-10-18 19:34:25

@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.

@GodsCrimeScene 2011-10-18 20:00:32

@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?

@Eric Lippert 2011-10-18 21:19:39

@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 2011-10-18 21:22:47

@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.

@Keith Thompson 2011-10-19 04:04:09

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).

@John Bode 2011-10-18 21:31:36

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).

@Mark Byers 2011-10-18 18:55:23

  • 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

@Matt Greer 2011-10-18 18:56:17

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.

@GodsCrimeScene 2011-10-18 19:25:05

@MarkByers Thanks Mark, this is a very good visual of what is happening.

@Random832 2011-10-18 21:26:11

@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.

@Brian 2011-10-19 16:41:40

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.

@Alok Save 2011-10-18 19:01:46

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."

@Mooing Duck 2011-10-18 19:05:26

Unspecified is not Undefined behavior, is it? I though unspecified meant Implementation Defined. (I could be wrong)

@Alok Save 2011-10-18 19:07:05

@James McNellis 2011-11-06 17:37:30

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.

@Lou 2011-10-18 18:57:15

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.

@n8wrl 2011-10-18 18:56:37

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.

@Luchian Grigore 2011-10-18 19:00:35

It's not undefined. In both cases, you only modify x once and the order of operation is completely defined by the standard.

@Lou 2011-10-18 19:00:55

@n8wrl Not sure I agree with that. Parenthesis always helps.

@Luchian Grigore 2011-10-18 19:03:29

Parenthesis do help, but that doesn't change the fact.

@Eric Lippert 2011-10-18 19:17:45

@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.

Related Questions

Sponsored Content

29 Answered Questions

[SOLVED] How to enumerate an enum

23 Answered Questions

[SOLVED] What is the "-->" operator in C++?

28 Answered Questions

[SOLVED] How do you set, clear, and toggle a single bit?

27 Answered Questions

[SOLVED] How to cast int to enum?

  • 2008-08-27 03:58:21
  • lomaxx
  • 1283326 View
  • 3025 Score
  • 27 Answer
  • Tags:   c# enums casting

76 Answered Questions

[SOLVED] How do I iterate over the words of a string?

  • 2008-10-25 08:58:21
  • Ashwin Nanjappa
  • 2143954 View
  • 2902 Score
  • 76 Answer
  • Tags:   c++ string split

10 Answered Questions

[SOLVED] Improve INSERT-per-second performance of SQLite?

7 Answered Questions

4 Answered Questions

[SOLVED] What does the C ??!??! operator do?

  • 2011-10-19 16:56:59
  • Peter Olson
  • 252045 View
  • 1918 Score
  • 4 Answer
  • Tags:   c operators trigraphs

2 Answered Questions

3 Answered Questions

[SOLVED] Prefix/Postfix increment operators

Sponsored Content