Does this code in C fall into the Undefined Behavior category?
Asked Answered
A

4

7

a is an array, foo is a function, and i is an int.

a[++i] = foo(a[i-1], a[i]);

Would the code above, have an Undefined Behavior?

The array indices ++i, i-1 and i, are guaranteed to be in the array range.

Aret answered 21/8, 2017 at 18:3 Comment(11)
What makes you think it might have an undefined behavior?Deficit
@Deficit Where is the sequence point in your opinion? What is the execution orde?Lucifer
@PeterJ Sequence point?Deficit
@Deficit Yes the point where the ++ will be evaluated. Before foo or after?Lucifer
Undefined because we don't know whether or not a[++i] will be evaluated before or aver a[i] or a[i - 1] In other words, we don't know the value of i.Pape
@PeterJ Well, i did not read the standard about that before so i did not knew. But Vlad seems to have the answer.Deficit
@Deficit if you do not know why do you comment this ironic way : What makes you think it might have an undefined behavior?. The question is what made you think that is notLucifer
It's actually tricky because function calls are sequence points (as well as returning from library functions). It doesn't help here because assignment is unsequenced, so you don't know when exactly the function call happens relative to incrementing i in a[++i].Overthecounter
Perhaps if you think it is ub I would think it is a good idea to rewrite the codeShin
@EdHeal that's what I'm going to do, It's just that code like this one is more compact and cleaner.Aret
What is a extra line of code? Especially when all readers can read it without the need to scratch their head? Surely cleaner code is what all understand.Shin
F
7

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]);
Felice answered 21/8, 2017 at 18:30 Comment(3)
Nice simplified example. Clarifies application of identical rules to slightly more complicated statement.Forester
what about this one : i++; a[i] = foo(a[i-2], a[i-1]); ?Aret
@BiteBytes; That's the correct way to do this and have defined behavior.Felice
C
5

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.

Chaw answered 21/8, 2017 at 18:8 Comment(11)
Just evaluation of operands being unsequenced doesn't mean it is UB. Every function call has its argument evaluation unsequenced.Preterhuman
@AjayBrahmakshatriya In the shown example it is the reason for the undefined behavior.Chaw
@VladfromMoscow; You are right in the shown example "unsequenced" is the reason for UB but not alone, along with modification of i. That's what Ajay is trying to say.Felice
@hacks It is clear without any comment. It is the reason for the question.Chaw
I always hear this notion of "sequencing" would you explain it furthermore please?Aret
Not exactly. The main issue here is that the standard does not specify when the increment takes place, only that ++i evaluates to i+1. Even if ++i was guaranteed to be evaluated first, this would still raise UB.Salters
More specifically, this is not about order of evaluation, but side effects.Salters
@Salters It is about whether the evaluations of the left and right operands are sequenced or not.Chaw
@Salters And there is clear written in the Standard according to the increment: "See the discussions of additive operators and compound assignment for information on constraints, types, side effects, and conversions and the effects of operations on pointers."Chaw
Does "unsequenced" mean there is no sequence point between each expression?Salters
@Salters If the evaluation of sub-expressions is unsequenced then there can not be a sequence point between them.:)Chaw
P
4

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)

Preterhuman answered 21/8, 2017 at 18:31 Comment(1)
Assignment is sequenced after computation of the value to be stored, but the determination of what object is assigned and, for compound-assignment operators, the determination of its initial value, aren't.Argali
L
3

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
    i = ++i + 1;
    a[i++] = i;
while allowing
    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

Leucomaine answered 21/8, 2017 at 18:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.