I am looking at the C11 Standard PDF (Page 463), and I am confused about the lexical grammar of declarations. It seems like code like this one seems to be grammatically valid in C11:
static;
static const int;
long int const typedef long;
even though there does not seem to be any use for such a declaration.
To be specific, it's this part here which confuses me:
declaration:
declaration-specifiers init-declarator-list<opt> ;
declaration-specifiers:
storage-class-specifier declaration-specifiers<opt>
type-specifier declaration-specifiers<opt>
type-qualifier declaration-specifiers<opt>
function-specifier declaration-specifiers<opt>
alignment-specifier declaration-specifiers<opt>
It seems like init-declarator-list in a declaration has been made explicitly optional. Is there any use for declarations without it?
CodePudding user response:
(That is not the C standard; it is an unofficial draft. And page 463 is non-normative text—it is just informative and not a binding part of the standard. This answer uses the official C 2018 standard, which has only technical corrections and clarifications from the 2011 standard.)
Speaking only to the formal grammar in the C standard, a declaration production, shown in C 2018 6.7 1, may be declaration-specifiers init-declarator-listopt ;
. In that, declaration-specifiers may produce storage-class-specifier declaration-specifiersopt, and init-declarator-list may be absent, giving storage-class-specifier declaration-specifiersopt ;
. Then the latter declaration-specifiers may be absent, giving storage-class-specifier ;
, which finally can produce static
;
.
However, the rules of the C standard go beyond the grammar. C 2018 6.7 2 says:
A declaration other than a static_assert declaration shall declare at least a declarator (other than the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration.
The declaration static
;
does not declare a declarator, a tag, or any member of an enumeration, so it violates this constraint. Then a conforming C implementation must issue a diagnostic message for it, although it may nonetheless accept it.
static
const
int
;
and long
int
const
typedef
long
;
also violate this constraint.
Further, C 2018 6.7.2 2 says:
At least one type specifier shall be given in the declaration specifiers in each declaration,…
Since static
;
has no type specifier, such as void
, int
, or long double _Complex
, it violates this constraint as well.
static
const
int
;
and long
int
const
typedef
long
;
do not violate this constraint.
It seems like init-declarator-list in a declaration has been made explicitly optional. Is there any use for declarations without it?
Yes, we can usefully declare structure, union, and enumeration tags, as in struct foo { int x; };
. This declares the type struct foo
and has no init-declarator (at the main level; there is one nested inside, of course).
We can also declare enumeration constants, as in struct foo { apple, banana };
. This has no init-declarator at any level.
The fact that there are useful declarations with no init-declarator meant that either init-declarator had to be kept optional in the production or the production had to be split into two productions, one of which allowed declaring structures and such with no init-declarator and one of which produced declarations with a mandatory init-declarator. Presumably the nuisance of complicating the grammar presentation was not considered worth the benefit of reducing the semantic constraints.