1
votes

I know that C and C++ and different languages, but the following applies to both.

TL/DR

I know that i = i++; is UB, because i is modified twice in the expression and C and C++ forbids it.

References :

C99 6.5 :

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings

C++ 11 - 1.9 15 :

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent, the behavior is undefined.

So I understand that *i = *i++ + *j++; causes UB, because post incrementation on i and affectation to *i may be unsequenced, and CLang issues a warning in C or C++ mode : warning: unsequenced modification and access to 'i' [-Wunsequenced] *i = *i++ + *j++;

But I do not understand the same warning on *i++ = *i + *j++;. Because here, we first compute the right part, affect it, and increment after the affectation.

And specs for both language say (same paragraph, just above) :

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator

END TL/DR

So the question is :

Is this line

*i++ = *i + *j++;

undefined behaviour, or is Clang (version 3.4.1) too conservative in issuing a warning on it ?

2
Assignment (=) is not a sequence point. - EOF
You are correct that (on the right) *i + *j++ must be evaluated before = is evaluated, but that doesn't dictate when (on the left) *i++ is evaluated. - Drew Dormann
Your TL/DR section is pretty much 90% of your question ;) - user4520
@EOF C++11 doesn't have sequence points - M.M

2 Answers

2
votes

The reason both

*i = *i++ + *j++;

and

*i++ = *i + *j++;

are undefined is that you are attempting to use the pointer i in an expression that's a value computation (dereference, *i) and an expression with a side effect (dereference and increment, *i++) without an intervening sequence point. Remember *i++ is evaluated as *(i++); you're incrementing the pointer value, not the thing being pointed to.

1
votes

Given x = *i + *i++; it would be legitimate for the sub-expression i++ to be broken down into seven pieces:

  1. Capture the state of the pointer for the *i++ part of the expression
  2. Read that address
  3. Capture the state of the pointer for the *i part of the expression
  4. Read that address
  5. Add the two values read
  6. Store the result in X.
  7. Put the pointer into an invalid state (e.g. for pointers larger than int, start by incrementing the bottom half, which might wrap)
  8. Put the pointer into a valid state which identifies the next object (finish writing an updated pointer value)

Steps #1 must be first, and must precede #7, which must in turn precede #8, but the compiler may rearrange any or all of the operations #2-#6 so they occur before, after, or between #7 and #8. Nothing in the Standard would require a compiler to make any effort to ensure that all other accesses to that same pointer will occur before #7 or after #8; if a compiler does happen to put a different access between steps #7 and #8, there's no telling what the consequences of using the temporarily-invalid pointer might be.