Introduction

A storage class is used to set the scope of a variable or function. By knowing the storage class of a variable, we can determine the life-time of that variable during the run-time of the program.

Syntax

Remarks

Storage class specifiers are the keywords which can appear next to the top-level type of a declaration. The use of these keywords affects the storage duration and linkage of the declared object, depending on whether it is declared at file scope or at block scope:

Keyword | Storage Duration | Linkage | Remarks | ––––– | –––––––– | ––––– | —–– |static | Static | Internal | Sets internal linkage for objects at file scope; sets static storage duration for objects at block scope. |extern | Static | External | Implied and therefore redundant for objects defined at file scope which also have an initializer. When used in a declaration at file scope without an initializer, hints that the definition is to be found in another translation unit and will be resolved at link-time. |auto | Automatic | Irrelevant | Implied and therefore redundant for objects declared at block scope. |register | Automatic | Irrelevant | Relevant only to objects with automatic storage duration. Provides a hint that the variable should be stored in a register. An imposed constraint is that one cannot use the unary & “address of” operator on such an object, and therefore the object cannot be aliased. |typedef | Irrelevant | Irrelevant | Not a storage class specifier in practice, but works like one from a syntactic point of view. The only difference is that the declared identifier is a type, rather than an object. |_Thread_local | Thread | Internal/external | Introduced in C11, to represent thread storage duration. If used at block scope, it shall also include extern or static.

Every object has an associated storage duration (regardless of scope) and linkage (relevant to declarations at file scope only), even when these keywords are omitted.

The ordering of storage class specifiers with respect to top-level type specifiers (int, unsigned, short, etc.) and top-level type qualifiers (const, volatile) is not enforced, so both of these declarations are valid:

int static const unsigned a = 5; /* bad practice */
static const unsigned int b = 5; /* good practice */

It is, however, considered a good practice to put storage class specifiers first, then any type qualifiers, then the type specifier (void, char, int, signed long, unsigned long long, long double…).

Not all storage class specifiers are legal at a certain scope:

register int x; /* legal at block scope, illegal at file scope */
auto int y; /* same */

static int z; /* legal at both file and block scope */
extern int a; /* same */

extern int b = 5; /* legal and redundant at file scope, illegal at block scope */

/* legal because typedef is treated like a storage class specifier syntactically */
int typedef new_type_name;

Storage Duration

Storage duration can be either static or automatic. For a declared object, it is determined depending on its scope and the storage class specifiers.

Static Storage Duration