12

Introduction

The C11 standard (ISO/IEC 9899:2011) has introduced a new definition of side effect sequencing within an expression (see related question). The sequence point concept has been complemented with sequenced before and sequenced after relations which are now the basis for all definitions.

Section 6.5 "Expressions", point 2 says:

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.

Later on, section 6.5.16 "Assignment operators", point 3 states:

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

Problem

The first quoted paragraph (6.5/2) is supported by two examples (same as in the C99 standard):

First example

a[i++] = i;  //! undefined
a[i] = i;    //  allowed

This can be easily explained with the definitions:

  1. If a side effect on a scalar object is unsequenced relative to (...) a value computation using the value of the same scalar object, the behavior is undefined. (6.5/2),
  2. The evaluations of the operands are unsequenced. [within an assignment] (6.5.16/3).

So, the side effect of i++ (LHS) is unsequenced with i (RHS), which gives undefined behaviour.

Second example

i = ++i + 1; //! undefined
i = i + 1;   //  allowed

This code, however, seems to result in a defined behaviour in both given cases as:

  1. the side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.

So, the execution of ++i + 1 shall precede the side effect of updating i, which means that there is not a side effect on a scalar object 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.

Question

It is easy to explain these examples with the terms and definitions presented by the C99 standard (see related question). But why is i = ++i + 1 undefined according to C11's terminology?

Community
  • 1
  • 1
Krzysztof Abramowicz
  • 1,556
  • 1
  • 12
  • 30

3 Answers3

7

Update

I am changing my answer here, this is not well defined in C11 although it is in C++11. The key here is that the result of ++i is not an lvalue and therefore does not require an lvalue-to-rvalue conversion after ++i is evaluated and so we can not be assured that the result of ++i will be read afterwards. Which is different than C++ and so the defect report I originally linked to hinges on this critical fact:

[...] the lvalue expression ++i and then do an lvalue-to-rvalue conversion on the result. guarantees that the incrementation side-effect is sequenced before the computation of the addition operation[...]

we can see this by going to the C11 draft standard section 6.5.3.1 Prefix increment and decrement operators which says:

[...]The expression ++E is equivalent to (E+=1).[...]

and then section 6.5.16 Assignment operators which says (emphasis mine going forward):

An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111 but is not an lvalue.[...]

and footnote 111 says:

The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type.

There is no requirement to read the object to determine it's value even if it is volatile.

Original Answer

As far as I can tell this is actually well defined and this example was removed from the C++ draft standard which uses similar language. We can see this in 637. Sequencing rules and example disagree which says:

the following expression is still listed as an example of undefined behavior:

i = ++i + 1;

However, it appears that the new sequencing rules make this expression well-defined:

and the resolution was to strike the prefix example and use the postfix example instead which is clearly undefined:

Change the example in 1.9 [intro.execution] paragraph 16 as follows:

i = ++i i++ + 1; // the behavior is undefined

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 1
    `++i` has different meaning in C and C++. – haccks Mar 24 '14 at 17:30
  • @haccks it looks like I was not correct, since the result of `++i` is not lvalue that changes the semantics. – Shafik Yaghmour Mar 24 '14 at 19:43
  • Yes. That's what I wanted to say :) – haccks Mar 24 '14 at 19:47
  • Does it mean that `i = ++i + 1` is well-defined in C++, while still having undefined behaviour in C11? After rewriting it to `i = (i+=1) + 1` we end up with two sequenced assignments, or a sub-expression sequenced before addition, right? So it should be well-defined also in C11. – Krzysztof Abramowicz Mar 27 '14 at 14:14
  • @KrzysztofAbramowicz the major difference is that in C++ assignment returns an `lvalue` while in C11 it specifically says `but is not an lvalue.`. So in C++ the fact that it returns an lvalue requires an `lvalue-to-rvalue` conversion of the result which requires a read of the the result which creates the sequenced before relationship that does not exist in C11. – Shafik Yaghmour Mar 27 '14 at 14:17
  • @KrzysztofAbramowicz [defect report 222](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#222) goes into these differences in more detail. I would not call this intuitive reading. – Shafik Yaghmour Mar 27 '14 at 14:19
  • In the C case I don't see how the lvalue conversion is relevant - Jens' answer seems to me to hit the key point – M.M Sep 26 '16 at 13:03
4

The standard stipulates for assignment (6.5.16) as you are citing correctly

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.

(The increment operator is not different, it is just an assignment in disguise)

This means that there are two value computations (left and right) and the side effect of the assignment is then sequenced after these. But it is only sequenced against the value computations, not against the side effects that these may have. So at the end we are faced with two side effects (of the = operator and the ++ operator) that are not sequence with respect to each other.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • My last thoughts on that topic went exactly in that direction... but I wasn't sure if the two-assignments interpretation is actually an argument for or against undefined behaviour :) Are you sure that _value computation_ does not include any _side effects_ implied by used operators? Are _operand evaluation_, _value computation_ and _side effect execution_ three different aspects of _expression evaluation_, which are subject to independent sequencing rules? – Krzysztof Abramowicz Mar 27 '14 at 21:40
  • @KrzysztofAbramowicz if it is not explicitly specified, then it is undefined. – Antti Haapala -- Слава Україні Jun 20 '18 at 07:55
3

But why is i = ++i + 1 undefined according to C11's terminology?

C11 says that the side effect on left i is sequenced but not the value computations (evaluations) of left and right i.
It is obvious that the side effect on LHS will take place after the evaluation of expressions on LHS and RHS.
To explain this a better example could be

int i = 1;
i = i++ + 3;

(First let's assume that this example will not invoke UB). Now the final value of i could be 4 or 2.
Case 1.
Left i is fetched and then it is incremented and 3 is added to it and finally 4 is assigned to i.
Case 2.
Left i is fetched and then 3 is added to it and then 4 is assigned to i and finally i is incremented. In this case the final value of i is 2.
Although the side effect on left i is sequenced the final value stored to i is not defined, i.e it is not necessarily by the assignment and hence the side effect on i is unsequenced.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • But why should I count LHS's `i` as a value computation? The current value doesn't matter and is not used anywhere. – Krzysztof Abramowicz Mar 24 '14 at 17:24
  • The LHS `i` will be evaluated before assigning the final value to it. – haccks Mar 24 '14 at 17:32
  • What does _evaluation_ actually mean in this context? Within the execution environment, `i` is just an arithmetic object in memory, or even a register -- what else can be done here beyond just assigning the new value (which is the side effect)? – Krzysztof Abramowicz Mar 24 '14 at 17:33
  • You need to fetch its address before assigning a value to it. – haccks Mar 24 '14 at 17:35
  • I updated my answer to clear this concept a bit more. – haccks Mar 24 '14 at 17:48
  • That's right, but fetching its address does not touch its value, so the fetch may interfere with the incrementation. Is it a standard requirement to count any identifier appearance as evaluation? – Krzysztof Abramowicz Mar 24 '14 at 17:48
  • 1
    @KrzysztofAbramowicz The quote you gave says that the assignment is sequenced after the *value computations*, not side effects of the expressions. This allows calculating the result of `++i` without performing the side effect of storing the calculated value until later. – ughoavgfhw Mar 24 '14 at 18:01
  • Sorry, I don't know about that. AFAIK, evaluation of left operand of `=` means fetching its address. – haccks Mar 24 '14 at 18:01
  • `++i` is defined as `i += 1`, whereas `i++` has no such equivalence, so I don't think it is useful to explain `++i` by analogy to a case involving `i++` – M.M Sep 26 '16 at 12:55
  • @M.M; It will have the same effect in case of `i = ++i + 3` too. Side effect on `i` is unsequenced. – haccks Sep 26 '16 at 13:22