a
is an array, foo
is a function, and i
is an int
.
a[++i] = foo(a[i-1], a[i]);
Would
The behavior is undefined, but it's not because of the modification of the same object twice between two sequence points but it is UB because the side effect on i
is unsequnced relative to the value computation of a[i-1]
and a[i]
using i
.
§6.5-p(2):
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.84)
For example, expression
a[i++] = i;
invokes undefined behavior. Same is true for
a[++i] = foo(a[i-1], a[i]);
Yes, it is Undefined Behavior.
Not because one is not allowed to read and write to the same variables between 2 sequence points but because of the rule that
Between any two sequence points, all the reads of a variable should directly be used in the computing the result of the write to the same variable.
Here the write to i
is i++
. The read on the same variable is in the arguments.
Although function call is a sequence point, the assignment isn't. So evaluation of array index can happen before evaluation of RHS.
The read on i
in foo(a[i-1], a[i])
doesn't directly contribute to the write and hence it is UB.
the relevant parts of the C99 standard are 6.5 Expressions, §2
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.
(Emphasis mine)
The behavior is undefined because the expressions a[++i]
, a[i-1]
, and a[i]
are unsequenced relative to each other.
Chapters and verses:
6.5 Expressions
...
2 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.84)
...
6.5.2.2 Function calls
...
10 There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.94)
...
6.5.16 Assignment operators
...
3 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. The type of an assignment expression is the type the left operand would have after lvalue conversion. 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.
84) This paragraph renders undefined statement expressions such as
while allowingi = ++i + 1; a[i++] = i;
...i = i + 1; a[i] = i;
94) In other words, function executions do not ‘‘interleave’’ with each other
...
111) 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.
C 2011 Online Draft
According to the C Standard (6.5.16 Assignment operators):
Semantics
3 ...The evaluations of the operands are unsequenced.
Thus this statement
a[++i] = foo(a[i-1], a[i]);
results in undefined behavior.