Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incrementing a variable used twice in an initializer list - undefined behavior?

Edit: Not already answered - the linked question was about ordinary r-values, initializer lists are a separate, if related concept.

Is this statement well-defined, or is using the prefix increment operator in an initializer list, on a variable that appears twice in the list, undefined behavior?

struct T t = { i, ++i };

I'm most interested in ANSI C, but it'd also be useful to know if other versions of C and/or C++ differ. And if similar constructs like the following are legal:

struct T t = { i, i++ };

struct T t = { ++i, ++i };

struct T t = { i++, ++i };

struct T t = { i++, i++ };
like image 330
Ray Hamel Avatar asked Sep 06 '16 03:09

Ray Hamel


3 Answers

C++11 and later

The behavior is well-defined for list initialization. According to the sequenced-before rules (since C++11):

10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.

So for struct T t = { i, ++i };, i will be evaluated at first, and then ++i, the order is well-defined. And all the other samples would be fine too.

Quotes from the C++ standard, $8.6.4/4 List-initialization [dcl.init.list]:

(emphasis mine)

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. — end note ]

like image 151
songyuanyao Avatar answered Oct 23 '22 21:10

songyuanyao


C

In C (not necessarily the same answer as for C++), there are no sequence points associated with the components of an initializer list.

The C11 standard, ISO/IEC 9899:2011, says in section §6.7.9 Initialization:

¶19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; 151)

151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.

That sounds promising, but…

¶23 The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.152)

152) In particular, the evaluation order need not be the same as the order of subobject initialization.

So, (in C) the order of evaluation is indeterminately sequenced, and you cannot rely on when the increments occur (or, in extreme cases not illustrated by the code in the question, whether the increments occur).

In C99 (ISO/IEC 9899:1999), the section number is §6.7.8, but paragraphs 19 and 23 have essentially the same content, except that the footnote numbers are different.

In C90 (ISO/IEC 9899:1990), the issue is not addressed explicitly.

C++

Judging from songyuanyao's answer, the rules in C++11 (and later) are different from those in C11. This sort of thing emphasizes that the languages C and C++ are different and makes writing comprehensive answers to questions tagged with both languages extremely difficult.

Closely related questions

There are at least two other questions related to side-effects (such as ++) in contexts other than initializers. They both should be read too. The second, in particular, is of interest to C++ users; the first is tagged C and not C++ and so is of most relevance to those interested in C.

  • Why are these constructs (using ++) undefined behaviour?

  • Undefined behaviour and sequence points

Both were pointed out by πάντα ῥεῖ in the comments.

like image 22
Jonathan Leffler Avatar answered Oct 23 '22 20:10

Jonathan Leffler


In C11 the behaviour of all these initialization is not undefined. See 6.7.9/23:

The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

The term indeterminately sequenced is defined as such (5.1.2.3):

Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B, but it is unspecified which.

In C99 the language used was not clearly worded as to whether it is the same situation, or undefined behaviour. In C89 the issue is not mentioned at all, so we should probably assume that in C89 these are undefined.

like image 6
M.M Avatar answered Oct 23 '22 20:10

M.M