Reputation: 41
I've been diving deeper into the C standard, and I'm confused about the way it talks about linkage and tentative definitions.
First, in this part of the standard it is stated that
extern (keyword) means static duration and external linkage (unless already declared internal)
static storage duration. The storage duration is the entire execution of the program, and the value stored in the object is initialized only once, prior to main function. All objects declared static and all objects with either internal or external linkage that aren't declared _Thread_local (since C11) have this storage duration.
external linkage. The identifier can be referred to from any other translation units in the entire program. All non-static functions, all extern variables (unless earlier declared static), and all file- scope non-static variables have this linkage.
so far we have that variables declared in file scope have static storage duration and external linkage by default. Also, objects with static storage duration are initialized to zero, before the program starts.
But, after reading this part (tentative definitions) and this part (declarations) I can't find where it says that objects with an explicit "extern" keyword are not allocated storage.
Please be careful about the difference between the "extern" keyword itself and the term "external declarations".
"External declarations" are defined as
At the top level of a translation unit (that is, a source file with all the #includes after the preprocessor), every C program is a sequence of declarations, which declare functions and objects with external linkage. These declarations are known as external declarations because they appear outside of any function.
regardless of the presence or absence of an explicit "extern" keyword.
I suppose that my concrete question is where in the standard does it say that file scope objects, that have an implicit external linkage by default, are not allocated storage if they are declared with an explicit "extern".
I know this is the case because if one declares the same identifier in multiple translation units all but one must have "extern" so as not to get a redefinition error.
Upvotes: 4
Views: 102
Reputation: 310960
In the C Standard (6.9.2 External object definitions ) there is written that
1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.
So if you will write at file scope
extern int x = 1;
then this declaration with the storage-class specifier extern
will be at the same time a definition of the object x
.
Otherwise if an object is declared at file scope without an initializer but with the storage-class specifier extern
then the compiler assumes that the object is defined in some other translation unit or in the same translation unit but somewhere else.
For example (here is declared a variable at file scope with internal linkage)
#include <stdio.h>
static int x = 10;
extern int x;
int main(void)
{
printf( "x = %d\n", x );
return 0;
}
If an object is declared at file scope without the storage-class specifier extern
then the compiler generates a tentative definition.
Upvotes: 1
Reputation: 223739
First, while cppreference.com has useful information it is not the C standard. The C11 standard can be found here.
This comes down to the difference between a declaration and a definition.
For an object, a declaration basically states that an object with a given type exists somewhere, while a definition is what actually allocates space for the object.
These terms are specified in sectin 6.7p5 of the C standard:
A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:
- for an object, causes storage to be reserved for that object;
- for a function, includes the function body;
- for an enumeration constant, is the (only) declaration of the identifier;
- for a typedef name, is the first (or only) declaration of the identifier.
By applying the extern
keyword, if there is no initializer then this constitutes a declaration, and a declaration does not allocate storage for an object. Section 6.9.2p1-2 spells this out:
1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.
2 A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier
static
, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.
A declaration with extern
and no initializer does not fit the above definition of a tentative definition or an external definition.
Section 6.9.2p4 gives examples of declarations and definitions:
int i1 = 1; //definition, external linkage static int i2 = 2; //definition, internal linkage extern int i3 = 3; //definition, external linkage int i4; //tentative definition, external linkage static int i5; //tentative definition, internal linkage int i1; //valid tentative definition, refers to previous int i2; //6.2.2 renders undefined, linkage disagreement int i3; //valid tentative definition, refers to previous int i4; //valid tentative definition, refers to previous int i5; //6.2.2 renders undefined, linkage disagreement extern int i1; //refers to previous, whose linkage is external extern int i2; //refers to previous, whose linkage is internal extern int i3; //refers to previous, whose linkage is external extern int i4; //refers to previous, whose linkage is external extern int i5; //refers to previous, whose linkage is internal
Upvotes: 3