A name in C has a scope, where it can be seen. It may have linkage, which says whether it refers to the same thing in other translation units. An object has a storage duration, which sets how long it exists. The storage-class specifiers control the last two (C17 6.2, 6.7.1).
Scope
| Scope | Applies to | Extends |
|---|---|---|
| File | Names declared outside any function | From the end of the declarator to the end of the translation unit |
| Block | Names declared in a block, and parameters of a definition | From the end of the declarator to the end of the block |
| Function | Labels only | The whole function body, even before the label |
| Function prototype | Parameter names in a prototype that is not a definition | To the end of the prototype |
An inner declaration hides an outer declaration of the same name in the same name space
(C17 6.2.1). A name’s scope starts just after its declarator, so in
int x = x; the second x is the new, uninitialised one.
A struct body does not open a new scope, so a tag or enumeration constant
declared inside one is visible in the surrounding scope.
Name spaces
The same identifier can mean different things at the same point if it is in different name spaces (C17 6.2.3). The kind of use decides which one is searched.
| Name space | Holds |
|---|---|
| Labels | Names used by goto |
| Tags | The names after struct, union and enum |
| Members | Each structure or union has its own |
| Ordinary | Objects, functions, typedef names and enumeration constants |
struct stat { int size; }; /* tag name space */
int stat(const char *, struct stat *); /* ordinary name space */
stat: /* label name space */
;
Storage-class specifiers
At most one may appear in a declaration, except _Thread_local, which may join
static or extern (C17 6.7.1). typedef is treated as
a storage-class specifier for grammar reasons only.
| Specifier | At file scope | At block scope |
|---|---|---|
| None | Object or function with external linkage | Automatic object with no linkage; a function declaration has external linkage |
auto | Not allowed | Automatic object; the default, so it is rarely written. C23 also uses it for type inference |
register | Not allowed | Automatic object; a hint that it is used often, and its address cannot be taken |
static | Object or function with internal linkage | Object with static storage and no linkage |
extern | External linkage, or the linkage of a prior visible declaration | Declares an object or function defined elsewhere, usually with external linkage |
_Thread_local | Object with thread storage | Requires static or extern |
constexpr (C23) | A constant object; internal linkage | A constant object |
register is the only storage class permitted on a parameter.
static int counter; /* internal linkage, static storage */
int shared; /* external linkage, static storage */
int tick(void)
{
static int calls = 0; /* no linkage, static storage: kept between calls */
return ++calls;
}
static means internal linkage at file scope, and static storage in a blockLinkage
Linkage decides whether two declarations refer to the same object or function (C17 6.2.2).
| Linkage | Meaning | Applies to |
|---|---|---|
| External | The same in every translation unit of the program | File-scope names with no static, and names declared extern |
| Internal | The same only within one translation unit | File-scope names declared static; C23 file-scope constexpr objects |
| None | Each declaration is a distinct entity | Block-scope objects without extern, parameters, and anything that is not an object or function |
A function declared with no storage class is treated as if declared extern.
If the same identifier in the same translation unit has both internal and external linkage,
the behaviour is undefined. In C, unlike C++, a const at file scope has
external linkage.
Each identifier with external linkage needs exactly one definition in the program, if it
is used (C17 6.9). A header should give only a declaration, and a single
.c file the definition:
/* config.h */
extern int verbose; /* declaration only */
/* config.c */
int verbose = 0; /* the one definition */
Tentative definitions
A file-scope object declaration with no initialiser and no storage class, or with
static, is a tentative definition (C17 6.9.2). If the translation unit
has no actual definition, the tentative definition acts as one with a zero initialiser.
int x; /* tentative definition */
int x; /* another: allowed */
int x = 7; /* the definition; the two above refer to it */
static int y; /* tentative: y is 0 if no other definition appears */
extern int z; /* a declaration, not a tentative definition */
Across translation units, a name with external linkage needs exactly one definition. Older
compilers merged tentative definitions from different files as a “common” symbol.
GCC stopped doing this by default in version 10, so two files that each contain
int x; now fail to link.
Storage duration and lifetime
An object’s lifetime is the part of execution during which storage is reserved for it (C17 6.2.4). Referring to an object outside its lifetime is undefined behaviour, and a pointer to it then has an indeterminate value.
| Duration | Lifetime | Declared by |
|---|---|---|
| Static | The whole program; initialised once before main runs | File scope, static, or block-scope extern |
| Automatic | Entry of the enclosing block to its exit | Block scope without static or extern |
| Thread | The life of a thread; initialised when the thread starts | _Thread_local |
| Allocated | From allocation until free | malloc, calloc, realloc, aligned_alloc |
- An automatic object is created each time its block is entered, recursively if necessary.
- A variable length array lives from its declaration until the end of its block.
- Since C11, a function result or other non-lvalue with a structure or union type containing an array has temporary lifetime, lasting until the end of the full expression.
- A compound literal in a block has automatic storage.
int *dangling(void)
{
int local = 1;
return &local; /* undefined when used: local no longer exists */
}
Inline functions
inline (C99) suggests that calls be as fast as possible (C17 6.7.4). It also
changes the linkage rules for the definition, and this is where C differs from C++.
| Form | Meaning |
|---|---|
static inline | An inline function with internal linkage. Each translation unit has its own copy. The usual way to put a function in a header |
inline with external linkage, no extern in the translation unit | An inline definition. It provides no external definition. Another translation unit must supply one if needed |
extern inline | An external definition, placed in one translation unit |
An inline function with external linkage may not define a modifiable static object, and may not refer to an identifier with internal linkage (C17 6.7.4).
/* maths.h */
inline int square(int x) { return x * x; } /* inline definition */
/* maths.c */
#include "maths.h"
extern inline int square(int x); /* emits the external definition */
Thread-local storage
_Thread_local (C11), spelled thread_local as a keyword in C23,
gives each thread its own instance of an object (C17 6.7.1, 6.2.4).
- It may be used only on objects, at file scope or, with
staticorextern, at block scope. - Every declaration of the same object must use it.
- The object is initialised when the thread starts. The initialiser must be a constant expression.
- Reading another thread’s object through a pointer is implementation-defined.
- In C11 and later,
errnohas thread storage. __STDC_NO_THREADS__is defined if threads are not supported.
constexpr (C23)
A constexpr object is a constant fixed at translation. It must be fully and
explicitly initialised with a constant expression, it may not be changed, and its value can
be used in other constant expressions. It cannot be a pointer other than a null pointer, and
it cannot have a variably modified, atomic, volatile or
restrict-qualified type. Unlike C++, functions cannot be
constexpr.