Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does 'a[i] = i;' always result in well defined behaviour?

There are several interesting questions raised here regarding undefined behaviour in C. One of them is (slightly modified)

Does the following piece of code result in undefined behaviour?

int i = 0, *a = &i;   // Line 1
a[i] = i + 1;         // Line 2

Since there is no concrete answer to this part of the question there, and I am interested in knowing the behaviour in C++, I am raising it again here.


Rule #2 from Undefined Behavior and Sequence Points says

Furthermore, the prior value shall be accessed only to determine the value to be stored

Clearly in the example above, the value is being accessed twice: a[i] (lhs) and i (rhs), and only one of them (the rhs) determines the value to be stored.

Does Line 2 violate the rule above and result in undefined behaviour in C++03?


There is some confusion as to whether i is modified at Line 2?

Yes it is modified!

like image 998
Lazer Avatar asked Mar 04 '12 15:03

Lazer


3 Answers

This will result in undefined behavior in C++03, and well-defined behavior in C++11.

C++03: Undefined Behvaior

From the C++03 standard, section 5 paragraph 4:

Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.

Note the second sentence: The previous value of i can only be used to determine the value to be stored. But here it is also used to determine the array index. So because this assignment will modify i, a[0] = i+1 is well defined, while a[i] = i+1 is not. Note that the assignment does not generate a sequence point: only the end of the full expression (the semicolon) does.


C++11: Well defined behavior:

C++11 got rid of the notion of sequence points, and instead defines which evaluations are sequenced before which.

From the standard, section 1.9 paragraph 15:

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. 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, the behavior is undefined.

Both operands of the assignment operator are sequenced before the actual assignment. So both a[i] and i+1 will be evaluated, and only then will i be modified. The result is well defined.

like image 118
interjay Avatar answered Oct 17 '22 02:10

interjay


int i = 0, *a = &i;

there is a sequence point between declarations, therefore no UB here. However take a note that it is a bad idea to declare/define variables that way. Any normal coding standard would tell you declare one variable per line.

a[i] = i;

The i is not changed in any way, therefore no UB here either.

like image 3
BЈовић Avatar answered Oct 17 '22 03:10

BЈовић


Let us decompose the expression a[i] = i + 1 will you ?

= -- [] -- a
  \     \_ i
   \
    \_ + -- i
         \_ 1

Effectively, a[i] refers to &i however note that neither a[i] nor i+1 modifies i. i is only modified when = (the assignment itself) is executed.

Since the operands of any function need be evaluated before this function takes effect, this is actually equivalent to:

void assign(int& address, int value) { address = value; }

assign(a[i], i + 1);

It is true that = is somewhat special in that it is built-in and does not result in a function call, still the evaluation of both operands are sequenced before the actual assignment, so they are first evaluated prior to i being modified, and a[i] (which points to i location) is being assigned to.

like image 2
Matthieu M. Avatar answered Oct 17 '22 04:10

Matthieu M.