Objects
Object declarations are much more complex than functions, because they not only determine linkage, but also may provide an object definition, a tentative definition, an initial value, and storage duration.
File-Scope Declarations
Objects declared at file-scope always have static storage duration, and, like functions, external linkage unless declared with the static
storage class specifier. There are three possible scenarios to consider:
The object has internal linkage and is defined in the current translation unit.
The object has external linkage and is defined in the current translation unit.
The object has external linkage and is defined in another translation unit.
each of which is denoted by a particular syntax,
The
static
storage class specifier, with or without an initializerNo storage class specifier, with or without an initializer.
The
extern
storage class specifier, without an initializer.
In any case, if an initializer is provided, it constitutes a definition, which is why case 3 requires the absence of an initializer–otherwise it is equivalent to case 2. If an initializer is not provided, the first two cases (static
or no storage class) constitute tentative definitions. If an object with a tentative definition is not explicitly defined by the end of a translation unit then it is implicitly defined as if it were initialized with a value of zero.
As an important edge-case, if an identifier is first declared static
, a subsequent extern
declaration of the same identifier does not change its linkage, and instead refers to the internally linked object from the earlier static
declaration.
int x; /* External, tentatively defined with an initial value of 0. */
static int y; /* Internal, tentatively defined with an initial value of 0. */
extern int z; /* External, not defined--must be defined in another translation unit */
extern int y; /* Internal, redeclaration retains linkage of earlier declaration. */
Students have often been admonished not to use global variables; to be clear, it is only providing an externally linked definition that is dangerous. There must be exactly one definition for every externally linked identifier that is ever accessed in an entire program; this is called the one definition rule (ODR). If two translation units (source files) in a program define an externally linked object with the same name, the program will not compile. This is known colloquially as “linker hell”.
Function Parameter Lists
Objects may also be declared within function parameter lists. These objects always have automatic storage duration and no linkage,
int sum(int a, int b) /* a and b are automatic variables */
{
return a + b;
}
The only storage class specifier that may be applied is register
and denotes that access to those objects should be as fast as possible. The register
storage class specifier prohibits taking the address of an object, which ensures that the compiler is able to place it in a register without ever storing it to memory, if it is able to so. For example, the following function might be faster because it could result in arguments passed in registers rather than on the stack: int sum(register int a, register int b) { return a + b; }
. Declaring an object as register
does not affect its linkage or storage duration.
Block-Scope Declarations
All other object declarations appear within the body of a function; for example,
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
Automatic local variables
When no storage class specifier is provided, the object has automatic storage duration and no linkage; these are also called local or automatic variables. If no initializer is provided, the default value of an automatic variable is indeterminate.
Stateful functions
If the storage class specifier is static
, the object still won’t have linkage, but will have static storage duration and will be zero-initialized if not provided an initial value. Static local variables preserve state between function calls,
int count()
{
static int i;
return i++;
}
int main()
{
printf("%d\n", count()); /* "0" */
printf("%d\n", count()); /* "1" */
printf("%d\n", count()); /* "2" */
}
External variables
The storage class extern
behaves the same as with file-scope declarations. This can be used to declare externally linked objects for use in a function without polluting the namespace at file-scope,
int accumulate(int x)
{
extern int sum;
sum += x;
return sum;
}
Note, unlike with file-scope declarations, it is illegal to initialize (define) in an extern
declaration inside a function; e.g.
void f() {
extern int y = 0; /* ERROR */
}