Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Reading" a POD preincrement result does not yield undefined behavior. Why exactly?

This is a stupid question. :)

[EDIT: stupid or not, this turned out to be a C++ peculiarity question, see UPDATE_2]

Suppose we have:

int a = 0; // line 1
int b = ++a; // line 2

What happens in line 2 is (note, numbers are just markers and do not specify exact order):

                      = [1: write result of (3) to result of (2)]
                     /\
[2: take "b" l-value] [3: convert result of (4) to an r-value ]
                      |
                      [4: take "a" l-value, "increment" and return it]

The "write" in (4) is "ordered" before the "read" in (3), and since there are no sequence points between, the side-effect is not guaranteed to happen before (3) (there is also a "read" inside (4) itself, but ordered before "write", so that does not yield UB).

So, where is the error in the above?

[UPDATE, aimed at not seasoned sequence-point lawyers :)]

In other words the problem is:

  1. There seems to be a "competition" whether the l-value-to-r-value conversion ("read") or increment ("write") side-effect takes place first.

  2. In C, that would give an UB, according to JTC1/SC22/WG14 N926 "Sequence Point Analysis"* (see, for example, EXAMPLE 5: int x,y; (x=y) + x; // UB).

  3. Note this would not be a case should postincrement be used since (3) and (4) will constitute a single [(3): take "a" l-value, convert it to r-value and return that r-value] with the "write" side-effect delayed until somewhere before the next sequence point

_

(*) This looks like the cleanest systematic rationale for the topic given by the C99 Standards Committee members.

[UPDATE_2]

  1. Lesson learned: never judge C++ by C rules :)). I did exactly that wondering why the N926 (which cleanly describes the C99 way of things) was "not clear enough" on the topic of preincrements yielding l-values.

  2. The question arises, how would one build a similar rationale for C++, as there isn't one, since even in the C case just interpreting the standard is pretty hard, and the C++ language standard is much more complicated and obscure.

[UPDATE_3]

There's a discussion dealing with some relevant topics (at least in the ~newer half) at "The undefinedness of a common expression.", plus the matter is discussed by the committee people here(see "222. Sequence points and lvalue-returning operators").

like image 463
mlvljr Avatar asked Mar 10 '10 20:03

mlvljr


1 Answers

I think the solution may be in the wording of "++i". It says "The value is the new value of the operand; it is an lvalue.". And behavior is undefined in 5/4 by "Furthermore, the prior value shall be accessed only to determine the value to be stored.".

So, we are not accessing the prior, but the new value. And then we may be fine. It seems to be a very thin line between undefined behavior and defined behavior though.

Actually "prior value" sounds to me like "value the object had at the previous sequence point". And if interpreted like that, then this construct looks undefined. But if we directly compare the wording of "++i" in 5.3/2 to 5/4, we are confronted by "new value" vs "prior value", and things are "bent" to defined behavior ("++i" would peek at the value of "i" at the next sequence point, and produce that value as the contents of the resulting lvalue of "++i").

like image 160
Johannes Schaub - litb Avatar answered Sep 19 '22 03:09

Johannes Schaub - litb