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

3

10

I was reading C Programming Language and found this sentence:

The commas that separate ... variables in declarations ... are not comma operators, and do not guarantee left to right evaluation.

If so, are they comma operators in this code?

int a=1, b=a+1, c=b+a+1, d=c+b+a+1;

I'm pretty sure that it will work. But if they're not comma operators and left to right sequence isn't guaranteed, then the above statement may fail, right?

Popelka answered 10/9, 2016 at 23:14 Comment(6)
Declarations also work sequentially. The more interesting situation is something like f(a += b, a).Singles
This will work. But, stylistically, I dislike using declarations with commas and prefer to do: int a = 1; int b = a + 1; int c = b + a + 1; int d = c + b + a + 1; [on separate lines]. Most style guides I've read prefer the split line approach too. IMO, using the commas is harder to read/understand [quickly] when looking at a lot of code. To me, the sequential intent is clearer. Others, seeing the commas might have to research this, just like your question is asking. Also note that I added some whitespace as visual aid as well.Steadfast
I suppose f(a += b, a) is a bad coding style as it has been talked a lot not to use like printf("%d, %d\n", a++, a++); and also the result isn't guaranteed by C standard. But I haven't see the same discussion about int a = 1, b = a + 1 ... and that's why I asked this question.Popelka
Agree that this isn't good coding style. But still would like to figure out whether it always works. :-)Popelka
@KerrekSB the commas in a function call are not 'comma operator' and there is no guarantee on sequence (in fact, in the 80's with steam-powered C compilers it was quite likely to be in reverse order, since params were pushed from right to left - but never any guarantees).Slope
@greggo: Which was my point :-)Singles
E
14

The comma in declarations is not a comma operator as found in expressions (declarations are not expressions, though initializations within a declaration are expressions). The quote in the question is accurate when it says the commas that separate declarations are not comma operators.

However, each declarator is complete at the comma or semicolon that follows it, so the variable definitions in the question are fully defined behaviour. The quote is incorrect when it implies that the left-to-right evaluation is not guaranteed — though it is a delicate piece of language dissection. If the commas were comma operators, then that fact would guarantee left-to-right evaluation; since they are not comma operators, then the left-to-right guarantee does not arise from the definition of the comma operator. However, because there are sequence points after each declarator, so the left-to-right valuation is separately guaranteed.

Finding the right wording in the standard to justify that claim is harder than I expected. It is actually in the section on declarators.

§6.7 Declarations

Syntax

declaration:
          declaration-specifiers init-declarator-listopt ;
          …

init-declarator-list:
          init-declarator
          init-declarator-list , init-declarator

init-declarator:
          declarator
          declarator = initializer

¶6 The declaration specifiers consist of a sequence of specifiers that indicate the linkage, storage duration, and part of the type of the entities that the declarators denote. The init-declarator-list is a comma-separated sequence of declarators, each of which may have additional type information, or an initializer, or both. The declarators contain the identifiers (if any) being declared.

¶7 If an identifier for an object is declared with no linkage, the type for the object shall be complete by the end of its declarator, or by the end of its init-declarator if it has an initializer; in the case of function parameters (including in prototypes), it is the adjusted type (see 6.7.6.3) that is required to be complete.

§6.7.6 Declarators

¶3 A full declarator is a declarator that is not part of another declarator. The end of a full declarator is a sequence point.

AFAICS, §6.7.9 Initializers does not add anything relevant.

The sequence point is crucial; it means that everything to the left is fully evaluated and side-effects are complete before continuing; it implies left-to-right sequencing, so the initialization in the question is fully defined.

It is slightly odd that the sequence point is after the full declarator and not the initializer; I don't think that it is significant, though.

Ephesians answered 11/9, 2016 at 1:10 Comment(9)
You are quoting from the C11 standard. I just searched the C17 and C2x standard drafts, and I can't find analogous wording. Specifically it seems that the following sentence was taken out: "The end of a full declarator is a sequence point." The C17 draft Annex C talks mentions sequence points between "full declarator[s] for a variably modified type", but I am a bit confused.Parodic
@LoverofStructure — Yes, 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". There probably is a document discussing and justifying that change (somewhere in the documents between C11 and C17 — there are a lot of them to check). I've not tracked that down yet.Ephesians
I think the document that lead to the changed text is DR 439. There might be follow-up documents that I haven't found.Steelmaker
@Steelmaker Follow-up Proposed resolution for DR 439, and Oct 2015 committee discussion and Proposed Technical Corrigendum for DR 439 in Defect Report Summary for C11 Version 1.9.Electrotherapeutics
I do not see anything in the quoted passages that implies the evaluation of initializers (or declarators or init-declarator items) is left-to-right. What might be taken to imply ordering is C 2018 6.2.4 6, discussing automatic objects: “… If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.” However, this only pins evaluation to reaching the declaration, not to subparts of the declaration.Degust
Ah, here it is: C 2018 6.8 3: “… 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.”Degust
C11 (and C99) §6.8 Statements and blocks ¶3 use the same wording as the quote by @EricPostpischil from C18: 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.Ephesians
The C23 draft has a more complicated preliminary sentence in §6.8¶3, but the previously quoted sentence now reads: 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 (the representation of objects without an initializer becomes indeterminate) 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..Very similar, but not identical.Ephesians
The question has been discussed and answered in terms of C17 here.Camellia
D
1

The initializers in int a=1, b=a+1, c=b+a+1, d=c+b+a+1; are sequenced in the order they appear because C 2018 (and C 2011) §6.8 ¶3 says:

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 it is used for b, a and b have values when used for c, and so on.

§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.

Degust answered 9/8, 2023 at 21:0 Comment(0)
A
-1

No, it may not, because in declarations comma acts as a sequence point, guaranteeing that evaluations will be performed in mentioned sequence. You can find more info here: Sequence point

Andress answered 10/9, 2016 at 23:29 Comment(3)
Please source your assertion, preferably from the standard.Unarm
The Wikipedia article does specify that there are sequence points at the commas between init-declarators but also that the commas are not comma operators.Ephesians
Found that and it did explain this issue well. But as the other answer explains more I think mark that as answer might help others more. Thanks!Popelka

© 2022 - 2024 — McMap. All rights reserved.