Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the definition int a = 0, b = a++, c = a++; have defined behavior in C?

Does the definition int a = 0, b = a++, c = a++; have defined behavior in C?

Or almost equivalently, does the comma in an object definition introduce a sequence point as for the comma operator in expressions?

Similar questions have been asked for C++:

  • Does `int a = 0, b = a` have undefined behavior?
  • Is the comma in a variable list a sequence point?
  • C++: variable declaration initialization order

The widely accepted answer for C++ is Yes, it is fully defined per paragraph 8/3 of the C++11 Standard:

Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself

Albeit this paragraph only refers to the syntax analysis phase and is not quite precise enough regarding the sequencing of operations at runtime.

What is the situation for the C language? Does the C Standard define the behavior?

A similar question was asked before:

Does the comma in a declaration for multiple objects introduce a sequence point like the comma operator?

Yet the answer seems to refer specifically to the C11 draft and may not hold for more recent versions of the C Standard as the wording of the informative Annex C has changed since the C11 draft and does not seem fully consistent with the Standard text either.

EDIT: of course such an initializer seems uselessly contorted. I definitely do not condone such programming style and constructions. The question arose from a discussion regarding a trivial definition: int res = 0, a = res; for which the behavior did not seem fully defined (!). Initializers with side effects are not so uncommon, consider for example this one: int arg1 = pop(), arg2 = pop();

like image 913
chqrlie Avatar asked Sep 03 '25 04:09

chqrlie


2 Answers

Does the definition int a = 0, b = a++, c = a++; have defined behavior in C?

Yes, because C 2018 6.8 3 says these initializations (not all, see bottom) are evaluated in the order they appear:

… The initializers of objects that have automatic storage duration, and the variable length array declarators of ordinary identifiers with block scope, are evaluated and the values are stored in the objects (including storing an indeterminate value in objects without an initializer) each time the declaration is reached in the order of execution, as if it were a statement, and within each declaration in the order that declarators appear. [Emphasis added.]

Also, 6.8 4 tells us that each initializer is a full expression and there is a sequence point after the evaluation of a full expression and evaluation of the next:

A full expression is an expression that is not part of another expression, nor part of a declarator or abstract declarator. There is also an implicit full expression in which the non-constant size expressions for a variably modified type are evaluated; within that full expression, the evaluation of different size expressions are unsequenced with respect to one another. There is a sequence point between the evaluation of a full expression and the evaluation of the next full expression to be evaluated.

Given both the above, the initializers are sequenced in the order they appear. a is initialized first and so has a value when a++ is evaluated for b, and the side effects for that are completed before the a++ for c begins, so the whole declaration is safe from the “unsequenced effects” rule in 6.5 2.

6.8 3 is a bit lacking for two reasons:

  • Initializers are not part of the grammar token declarator (they are part of the init-declarator, a containing token of declarator). However, this seems like a wording issue, and we can take the initializers to be associated with their declarators.
  • It does not specify ordering between the expressions in a declarator (such as sizes for variable length arrays) and its initializer(s).

Also note that not all initializers are evaluated in the order they appear in a declaration. 6.7.9 23 discusses initializers for aggregates and unions and says:

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.

History

The wording in 6.8 3 quoted above goes back to C 1999. In C 1990, it had this form in 6.6.2, which is about compound statements:

… The initializers of objects that have automatic storage duration are evaluated and the values are stored in the objects in the order their declarators appear in the translation unit.

like image 51
Eric Postpischil Avatar answered Sep 04 '25 16:09

Eric Postpischil


"Does the definition int a = 0, b = a++, c = a++; have defined behavior in C?"...

In the current C standard ISO/IEC9899:2017, program execution is covered in section §5.1.2.3 (3) which includes discussion on sequencing and side effects. The source text is reproduced below for reference.

Summarizing from the sections of text below, initializers in a declaration statement are sequenced, guaranteeing that the initializer expressions in the declaration posted...

 int a = 0, b = a++, c = a++;

which describes an "...init-declarator-list [which] is a comma-separated sequence of declarators," (section 6.7 Declarations)
...will not invoke undefined behavior, or even indeterminate results. Each comma separated expression is guaranteed to be sequenced starting from left, and not moving to the right until all evaluations and side-effects for the current expression are resolved and complete. In this way the results of each expression is fully defined.

From §5.1.2.3

"Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread, which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. (Conversely, if A is sequenced before B, then B is sequenced after A.) If A is not sequenced before or after B, then A and B are unsequenced. Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B, but it is unspecified which.13) The presence of a sequence point between the evaluation of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B. (A summary of the sequence points is given in annex C.)"

The relevant paragraph provided in Annex C:

"The following are the sequence points described in 5.1.2.3:"+ (3) ...

"Between the evaluation of a full expression and the next full expression to be evaluated. The following are full expressions: a full declarator for a variably modified type; an initializer that is not part of a compound literal (6.7.9); the expression in an expression statement (6.8.3); the controlling expression of a selection statement (if or switch) (6.8.4); the controlling expression of a while or do statement (6.8.5); each of the (optional) expressions of a for statement (6.8.5.3); the (optional) expression in a return statement (6.8.6.4)".
(emphasis mine)

like image 27
ryyker Avatar answered Sep 04 '25 17:09

ryyker