Reputation: 13
Expanding the syntax of "declaration-specifiers" from the C standard I get a syntax that permits many combinations of specifiers that are semantically illogical. Has anyone written a more precise syntax that permits fewer contradictory combinations? It appears the unordered nature of the syntax (presumable intended so the programmer is not burdened with having to remember an order of specifiers) makes this complicated. It would help if there some notation for specifying "any of a choice of specifiers but only one occurance of each of a choice is allowed" (that isn't particularly well worded). Or am I barking up the wrong tree? if, for example, there is a nice concise set of semantic rules that specify which combinations are (not) permitted together. From my interpretation of the C standard the syntax is
declaration-specifiers: (storage-class-specifier|type-specifier|type-qualifier|function-specifier|alignment-specifier)+
storage-class-specifier: 'typedef'|'extern'|'static'|'_Thread_local'|'auto'|'register'
type-specifier: 'void'|'char'|'short'|'int'|'long'|'float'|'double'|'signed'|'unsigned'|'_Bool'|'_Complex' | '_Atomic' '(' type-name ')' | struct-or-union-specifier | enum-specifier | typedef-name
type-qualifier: 'const'|'restrict'|'volatile'|'_Atomic'
function-specifier: 'inline' | '_Noreturn'
alignment-specifier: '_Alignas' '(' type-name | constant-expression ')'
Upvotes: 1
Views: 150
Reputation: 15042
The C standard implies no rule about the order how to implement declaration specifiers, storage-class specifiers, type specifiers, datatype specifiers or alignment specifiers inside of the declaration of a certain object.
You can intermix them arbitrary.
Quote from the ISO/IEC 9899:2018, Section 6.7.2/2
"At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name. Each list of type specifiers shall be one of the following multisets (delimited by commas, when there is more than one multiset per item); the type specifiers may occur in any order, possibly intermixed with the other declaration specifiers."
The only constraints are that at least one type specifier and at least one identifier has to be provided and the identifier has to stand on the right hand side of the declaration.
By the way there is another thing to consider, although it is syntactically correct yet. It is obsolete to place a storage-class specifier anywhere else than the first position.
Quote from the ISO/IEC 9899:2011, Section 6.11.5 - Storage class specifiers:
"The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature."
Thus,
static const long long int a;
can be written as:
static long int const long a;
or
static long const long int a;
or
static int const long long a;
But to keep your code readable and clear for future readers, you should use the common convention.
Upvotes: 0
Reputation: 123468
There are separate constraints in the language definition that specify which combinations of declaration specifiers are legal; it is not specified within the grammar itself. For example:
6.7.2 Type specifiers
...
Constraints
2 At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name. Each list of type specifiers shall be one of the following multisets (delimited by commas, when there is more than one multiset per item); the type specifiers may occur in any order, possibly intermixed with the other declaration specifiers.
— void
— char
— signed char
— unsigned char
— short, signed short, short int, or signed short int
— unsigned short, or unsigned short int
— int, signed, or signed int
— unsigned, or unsigned int
— long, signed long, long int, or signed long int
— unsigned long, or unsigned long int
— long long, signed long long, long long int, or signed long long int
— unsigned long long, or unsigned long long int
— float
— double
— long double
— _Bool
— float _Complex
— double _Complex
— long double _Complex
— atomic type specifier
— struct or union specifier
— enum specifier
— typedef name
While all that probably could be encoded in the grammar, it would be cumbersome. Making it a separate constraint makes life a little easier, I think (and also makes it easier to introduce new combinations, or deprecate old ones).
Upvotes: 1