In a statement like

static int a, b, c;

is static applied to the definition of all 3 variables or just the first one? What about other keywords like volatile or mutable.

If the rule is different for different keywords, how does it differ?

For example, I know that const is distributed, but pointer is not, e.g.

int * a, b;

is the same as

int * a;
int   b;

Note: I tried asking a similar question using const as an example instead of static, and it was misunderstood. I'd like to know what the general rule is for all applicable keywords according to the standard.

Let's put it this way. How is a definition/declaration parsed in any case where the variable type specifier consists of multiple words followed by a comma separated list of variables?

1 Answers

melpomene On Best Solutions

All initial typey words apply to all variables or, more properly speaking, declarators. A declarator is a pseudo-expression used in declarations.

For example:

static int volatile long a, b[10], *&c, *(*const d)(float);
// typey words

//                       ^  ^^^^^  ^^^  ^^^^^^^^^^^^^^^^^^
//                       4 declarators


  • a is a static volatile long int
  • b is a static array[10] of volatile long int
  • c is a static reference to a pointer to volatile long int
  • d is a static const pointer to a function (taking float) returning a pointer to a volatile long int

(Also, the order of the initial typey words does not matter.)


In C++98 (ISO/IEC 14882:1998) (outdated, I know, but this part of the language hasn't fundamentally changed since then) we have (all emphasis mine):

In [dcl.dcl]:

                   decl-specifier-seqopt init-declarator-listopt


  1. Each init-declarator in the init-declarator-list contains exactly one declarator-id, which is the name declared by that init-declarator and hence one of the names declared by the declaration. The type-specifiers (7.1.5) in the decl-specifier-seq and the recursive declarator structure of the init-declarator describe a type (8.3), which is then associated with the name being declared by the init-declarator.

(The standard prefers the term "declaration specifier" over "typey words", but it comes to the same thing.)

This says all the type specifiers are combined with each declarator to form the type of each name being declared. Type specifiers include const, volatile, signed, unsigned, as well as all basic types (void, int, wchar_t, ...) and user-defined class and enum and typedef names (see [dcl.type]).

Similar language applies to storage class specifiers, which are listed separately (auto, register, static, extern, mutable) in []:

  1. [...] The storage-class-specifier applies to the name declared by each init-declarator in the list and not to any names declared by other specifiers. [...]

Similarly, for typedef we have (in [dcl.dcl]):

  1. If the decl-specifier-seq contains the typedef specifier, the declaration is called a typedef declaration and the name of each init-declarator is declared to be a typedef-name, synonymous with its associated type (7.1.3). [...]

Later on, the chapter about declarators ([dcl.decl]) sums up the general principle:

  1. The two components of a declaration are the specifiers (decl-specifier-seq; 7.1) and the declarators (init-declarator-list). The specifiers indicate the type, storage class or other properties of the objects, functions or typedefs being declared. The declarators specify the names of these objects, functions or typedefs and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning). [...]

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

Equivalent sections in the current draft: