How do Prefix (++x) and Postfix (x++) operations work?

早过忘川 提交于 2021-02-05 09:28:55

问题


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?


回答1:


  • 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



回答2:


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.




回答3:


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




回答4:


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.




回答5:


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




回答6:


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.




回答7:


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



来源:https://stackoverflow.com/questions/61172254/operator-precedence-in-c-sharp-doesnt-work-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!