19
Does the definition int a = 0, b = a++, c = a++;
have defined behavior in C?
Or almost equivalently, does the ,
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?
EDIT: A similar question was asked before:
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 and does not seem fully consistent with the Standard text either.
10
2 Answers
Reset to default
11
"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)
7
-
2
Nothing in the passages quoted in this answer says the initializers are evaluated in the order they appear in the source code. Where the text says “The init-declarator-list is a comma-separated sequence of declarators,” that just means they are a sequence of source code. It does not speak to sequencing of evaluation.
– Eric Postpischil14 hours ago
-
1
@EricPostpischil I don't think the quote you've quoted is what the answer is relying on to establish that their evaluation is sequenced? The paragraph quotes from Annex C (at the bottom of the answer) says that there is a sequence point between each full expression, and that declarators and initializers are full expressions. The quote you quoted just establishes that these things are declarators (and I suppose that they have a sequence so that "between" has meaning, so that there can be sequence points between them).
– Ben8 hours ago
-
@Ben: There is nothing else in the quoted passages that mentions any sequence or sequence order of initializer evaluation.
– Eric Postpischil8 hours ago
-
@EricPostpischil Doesn't the bottom paragraph say there are sequence points between them, and the paragraph above that say that if there are sequence points between any two evaluations A and B then "every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B". I'm not by any means super experienced at reading C standards language, but that seemed reasonably straightforward to me.
– Ben8 hours ago
-
@Ben The problem is that the bottom paragraph is from an informative annex in the standard. The C behavior should be fully defined by the normative clauses.
– nielsen4 hours ago
7
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.
2
-
IMHO this is the correct answer since it answers the question without depending on informative text in the standard.
– nielsen4 hours ago
-
It may be mentioned that 6.8-5 (as a NOTE) explicitly specifies "an initializer that is not part of a compound literal" as being a full expression.
– nielsen3 hours ago
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
The duplicate question is only answered for C11. The corresponding clause has been changed in C17. Appendix C in C17 still lists "an initializer that is not part of a compound literal" as a full expression which introduces a sequence point, but even though section 6.7.9 is given as reference, I fail to find a supporting normative clause in that section.
23 hours ago
The accepted answer in the referenced duplicate question includes some ambiguity wrt the source information used to assert the answer. In comments, the author of the accepted answer says "I quoted C11 because C17 wasn't finalized when I wrote the answer. You're right that neither C17 nor C23 (N2176 and N3054 from the WG14 Document Log) seems to include the sentence "The end of a full declarator is a sequence point".". If you would like to expand this question to explicitly include the newer standards, I will remove the closed tag. Ping me.
23 hours ago
@nielsen – good point. I had already noticed the ambiguities was drafting my comment as you posted yours 🙂
22 hours ago
@ryyker: I amended the question in this direction, thank you for your support trying to solve the issue formally.
22 hours ago
You're welcome. Expanding the question was a good move in light of the age of the previously linked duplicate, and the ambiguity caused by the gap in time between C11 and C17 (which was not yet ratified), which was evidently showing some gaps wrt sequence points in declarators at that time.
21 hours ago
|
Show 5 more comments