3

As everyone knows, this loops through zero:

while (x-- > 0) { /* also known as x --> 0 */
  printf("x = %d\n", x);
}

But x = x-- yields undefined behaviour.


Both examples need some 'return' value of x--, which is not there I guess. How can it be that x-- > 0 is defined but x = x-- is not?

Community
  • 1
  • 1
  • 4
    I hope you understand that aside from the formal reason, writing `x = x--;` simply makes no sense. Either write `x--;` by itself or replace the second `-` in `x = x--;` with a `1`... – R.. GitHub STOP HELPING ICE Feb 07 '11 at 17:33
  • @R. I might better have asked it differently: 'Why isn't `x-- > 0` undefined like `x = x--`?' –  Feb 07 '11 at 17:35
  • 2
    From a formal perspective, because `x` is only modified once. From a common-sense standpoint, because this is the whole point of the `--` operator: it modifies and lvalue and yields the old value. Nothing fishy is going on. – R.. GitHub STOP HELPING ICE Feb 07 '11 at 18:35

5 Answers5

18

Because in x = x-- you're modifying the value of x twice without an intervening sequence point. So the order of operations is not defined. In x-- > 0 the value of x is modified once, and it is clearly defined that result of evaluating x-- will be the value of x before the decrement.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
  • Ah, so `x--` evaluates to `x - 1`, but the order in `x = x--` isn't defined? –  Feb 07 '11 at 17:33
  • @Radek S: Huh? `x--` does not return `x` at all. `x--` returns the *original value* of `x`. Firstly, the original value is the value *before* the decrement (where did you "after" come from?). Secondly, it is not `x`. It is the original *value* of `x`. I hope you understand the difference between a *variable* and the *value* of a variable. – AnT stands with Russia Feb 07 '11 at 17:35
  • 2
    @Radek S: Nothing in `x = x--` is defined. The language says that the `x = x--` is undefined. That means that the whole thing is undefined, not just some "order" of something. – AnT stands with Russia Feb 07 '11 at 17:37
  • 2
    @hauntsaninja: Seeming to work correctly is a reasonable result for undefined behavior. It could also return 42 and be compliant. There is no reasonable expectation for a "correct" result in an expression like this; it depends on what your expectation of the order of evaluation is. – Fred Larson Feb 07 '11 at 17:38
  • @Radek S: However, the rationale behind all this is that the moment and the order in which *side-effects* take place is not defined. Side-effects is the key issue here. `x = x--` has two side-effects, which my occur at any moment in any order. This is what makes `x = x--` undefined. – AnT stands with Russia Feb 07 '11 at 17:39
  • Strictly speaking, there's no requirement for the `--` to occur *after* evaluating `x` for comparison. The compiler might decide to decrement `x` first, and then use the value of `x + 1` for comparison purposes. That will also work correctly. Postfix `--` means that the *original* value is used in comparison. How that original value is obtained - be preserving the old value or by deriving the old value from the new value - is not specified and does not really matter. – AnT stands with Russia Feb 07 '11 at 19:26
  • @AndreyT: I agree. Would you say that my edit is more accurate? – Fred Larson Feb 07 '11 at 19:29
7

I don't know where you got that idea about "need some 'return' value of x--, which is not there". Firstly, it is not exactly clear what you mean. Secondly, regardless of what you mean this doesn't seem to have anything to do with the source of undefined behavior in x = x--.

x = x-- produces undefined behavior because it attempts to modify x twice without an intervening sequence point. No "need" for any "return value" is involved here.

The underlying problem with x = x-- is that it has two side-effects that occur at undefined moments in undefined order. One side-effect is introduced by the assignment operator. Another side-effect is introduced by postfix -- operator. Both side-effects attempt to modify the same variable x and generally contradict each other. This is why the behavior in such cases is declared undefined de jure.

For example, if the original value of x was 5, then your expression requires x to become both 4 (side-effect of decrement) and 5 (side-effect of assignment) at the same time. Needless to say, it is impossible for x to become 4 and 5 at the same time.

Although such a straightforward contradiction (like 4 vs 5) is not required for UB to occur. Every time you have two side-effects hitting the same variable without intervening sequence point, the behavior is undefined, even if the values these side-effects are trying to put into the variable match.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

In order to understand this you need to have a basic understanding of sequence points. See this link: http://en.wikipedia.org/wiki/Sequence_point

For the = operator there is no sequence point, so there is no guarantee that the value of x will be modified before it is again assigned to x.

When you are checking the condition in the while loop x-- > 0, x-- is evaluated and the value is used in the relational operator evaluation so there is no chance of undefined behaviour because x is getting modified only once.

Louis Marascio
  • 2,629
  • 24
  • 28
Sunil
  • 31
  • 1
2

Just to add something to other answers, try reading this wikipedia page about sequence points.

BlackBear
  • 22,411
  • 10
  • 48
  • 86
1

I suggest reading https://stackoverflow.com/a/21671069/258418. If you chuck together that = is not a sequence point, and the compiler is free to interleave operations, as long as they are not separated by a sequence point from the answers linked by you, you see that i.e. the following two sequences would be legal:

load i to reg
increment i
assign reg to i
=> i has previous value of i

load i to reg
assign reg to i
increment i
=> i has value of previous value of i + 1

In general: avoid assigning (this includes modiying by pre/post ++/--) to the same variable twice in one expression.

Community
  • 1
  • 1
ted
  • 4,791
  • 5
  • 38
  • 84