According to the c documentation on storage class specifiers (using the well known cpp reference website): https://en.cppreference.com/w/c/language/storage_duration, "Storage-class specifiers appear in declarations.... if no storage-class specifier is provided, the defaults are:
- extern for all functions
- extern for objects at file scope
- auto for objects at block scope"
So, when we have a definition of a file scope variable, i, in one file:
file1.c
int i = 0;
How would it even be possible at file scope (in another file) to have both (1) no storage class specifier and (2) only a declaration of the variable. Because a file scope variable is initialized and defined if there is no extern keyword it seems that the variable can only either be defined resulting in a compiler error:
file2.c
int i;
or declared with extern keyword:
file2.c
extern int i;
Thus, I don't understand how their can both be a declaration and no storage class specifier at file scope. I may be getting too much into the semantics but it doesn't make sense to have a default declaration at file scope that defaults to extern. Any help would be appreciated!
cppreference.com is not an authoritative reference for C or C++ and has numerous deficiencies. When issues arise in it, you should refer to the C or C++ standards.
Although the text you elided says “The storage-class specifiers determine two independent properties of the names they declare: storage duration and linkage,” this passage is constructed to give the (incorrect) impression that if no storage-class specifier is provided, the semantics are as if one of the defaults it lists were provided as a storage-class specifier.
That is false. In various cases, the semantics of a declaration without any storage-class specifier have differences from a declaration with storage-class specifier.
When
int i;appears at file scope, it has static storage duration and external linkage, the same as ifextern int i;had been used. However, these are different in thatint i;is a tentative definition andextern int i;is a declaration.Note that in spite of what its name suggests, a tentative definition is not actually a definition, just as a prospective employee on a job interview is not an employee.
int i;is a declaration that is not a definition.Consider a translation unit containing:
In this case,
int i = 3;is a definition, and the tentative definitions have no effect other than as declarations. Their semantics are governed by the rules for declarations, and nothing in the C standard says they are definitions ofi.Alternatively, consider:
In this case, these tentative definitions are again declarations, but C 2018 6.9.2 2 tells us that, in effect, a definition is created for
iat the end of the translation unit:int i;has no storage class specifier and is a declaration that is not a definition. How it behaves is governed by the rule in C 2018 6.9.2 2; it does not behave the same way as eitherextern int i;orint i = 0;.C does not have a global scope. It has file scope, which is different. In a programming language with global scope, declaring an identifier at global scope makes it known throughout the program. In C, declaring an identifier at file scope makes it known only in the translation unit of the declaration, not the entire program. To make it known elsewhere in the program, it must be declared elsewhere and linked.