Declarators

Following the declaration specifiers, a comma-separated list of declarators introduce identifiers for objects and functions, or corresponding type aliases when the typedef specifier is used. Each declarator may individually derive pointer, array, and function types from the base type in the declaration specifiers. The declarator grammar allows for recursive, repeated derivation of the pointer, array, and function types from already derived types,

init-declarator-list:

init-declarator

[ , init-declarator ]…

init-declarator:

declarator

[ = initializer ]…

Type Derivation

As mentioned previously, derivation of pointer, array, and function types is performed in each individual declarator. The most basic declarator is just the name of the identifier being declared,

declarator:

identifier

Grouping

Any declarator may be enclosed in parentheses, so that any derivations inside the parentheses are carried out before any derivations outside the parentheses.

declarator:

( declarator )

Pointer Derivation

Any declarator may be modified by prepending an asterisk (*) and optional type qualifiers, to derive a pointer type,

declarator:

*

type-qualifier

declarator

The type qualifiers apply to the pointer, itself, rather than the object it points at, which may have its own type qualifiers.

Array Derivation

Any declarator may be modified by appending square brackets, optionally enclosing a size expression, to derive an array type,

declarator:

declarator [ ]

declarator [ size-expression ]

If a size expression is not provided, the array type is incomplete.

Function Derivation

Any declarator may be modified by appending parentheses, optionally enclosing a list of types and/or declarations representing parameters, to derive a function type,

declarator:

declarator ( )

declarator ( type-or-declaration-list )

Initialization

Initialization is a special form of assignment that may be performed when an object is declared. Any objects declared as const must be initialized, since it is illegal to later change their values. Additionally, aggregate types can be list-initialized to assign many members or elements–an operation that would normally require assignment of each element or member, individually.

Scalar initialization

A declarator for an object may provide an initial value as an assignment expression for scalar types, in the form,

initializer:

expression

As mentioned, this is required for any const objects so that they have an initial value.

Aggregate initialization

Aggregate types are initialized using brace-enclosed list-initialization,

initializer:

{ initializer-list }

initializer-list:

designated-initializer

[ , designated-initializer ]…

designated-initializer:

[ designator = ]

initializer

Successive elements or members of an array or structure are assigned using values from the initializer list. Specific elements or members may be designated and initialized as well.

For an array, elements are designated by their indices enclosed in square brackets,

designator:

[ index-expression ]

For a structure, members are designated as a . (dot) followed by then name of the member,

designator:

. member-identifier

As with enumerations, assignment of successive elements starts from the designated element. Remaining members or elements are zero-initialized. Note, if an initializer is not provided, then the values are indeterminate; to zero-initialize an entire array or structure, use ={0}.

For example,

int num_list[5] = {1, [2]= 2, 3, 5}; /* num_list[1] implicitly assigned 0 */

/* num_list contains: 1, 0, 2, 3, 5; */

struct cartesian {
   int x, y;
};

struct cartesian coords = {.x = 1, .y = 3}; /* Member designators */
struct cartesian origin = {0}; /* `x` explicitly 0, y implictly 0 */

Automatically sized Arrays

Recall that an array derivation with no size expression is an incomplete type. One way the type can be completed is by providing an initializer–the number of elements is deduced from the initializer.

/* The size of values is implied to be 3 elements */
int values[] = {1, 2, 3};

Initializing character arrays

Finally, there is a special mechanism for initializing arrays of char (char[]), where a string may be used in place of a brace-enclosed initializer list. If the array size is specified it must be large enough to hold the string; otherwise the implicit size is one character longer than the string, to add a terminating null byte,

char s[] = "Hello!";
/* is equivalent to... */
char s[] = {'H', 'e', 'l', 'l', 'o', '!', 0};

char s[6] = "Hello!";
/* is equivalent to... */
char s[6] = {'H', 'e', 'l', 'l', 'o', '!'};