Why the following doesn't compile?
...
extern int i;
static int i;
...
but if you reverse the order, it compiles fine.
...
static int i;
extern int i;
...
What is going on here?
Why the following doesn't compile?
...
extern int i;
static int i;
...
but if you reverse the order, it compiles fine.
...
static int i;
extern int i;
...
What is going on here?
This is specifically given as an example in the C++ standard when it's discussing the intricacies of declaring external or internal linkage. It's in section 7.1.1.7, which has this exert:
static int b ; // b has internal linkage
extern int b ; // b still has internal linkage
extern int d ; // d has external linkage
static int d ; // error: inconsistent linkage
Section 3.5.6 discusses how extern
should behave in this case.
What's happening is this: static int i
(in this case) is a definition, where the static
indicates that i
has internal linkage. When extern
occurs after the static
the compiler sees that the symbol already exists and accepts that it already has internal linkage and carries on. Which is why your second example compiles.
The extern
on the other hand is a declaration, it implicitly states that the symbol has external linkage but doesn't actually create anything. Since there's no i
in your first example the compiler registers i
as having external linkage but when it gets to your static
it finds the incompatible statement that it has internal linkage and gives an error.
In other words it's because declarations are 'softer' than definitions. For example, you could declare the same thing multiple times without error, but you can only define it once.
Whether this is the same in C, I do not know (but netcoder's answer below informs us that the C standard contains the same requirement).
For C, quoting the standard, in C11 6.2.2: Linkage of identifiers:
3) If the declaration of a file scope identifier for an object or a function contains the storage-class specifier
static
, the identifier has internal linkage.4) For an identifier declared with the storage-class specifier
extern
in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.
(emphasis-mine)
That explains the second example (i
will have internal linkage). As for the first one, I'm pretty sure it's undefined behavior:
7) If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.
...because extern
appears before the identifier is declared with internal linkage, 6.2.2/4 does not apply. As such, i
has both internal and external linkage, so it's UB.
If the compiler issues a diagnostic, well lucky you I guess. It could compile both without errors and still be compliant to the standard.
In Microsoft Visual Studio, both versions compile just fine. On Gnu C++ you get an error.
I'm not sure which compiler is "correct". Either way, having both lines doesn't make much sense.
extern int i
means that the integer i
is defined in some other module (object file or library). This is a declaration. The compiler will not allocate storage the i
in this object, but it will recognize the variable when you are using it somewhere else in the program.
int i
tells the compiler to allocate storage for i
. This is a definition. If other C++ (or C) files have int i
, the linker will complain, that int i is defined twice.
static int i
is similar to the above, with the extra functionality that i
is local. It cannot be accessed from other module, even if they declare extern int i
. People are using the keyword static (in this context) to keep i localize.
Hence having i
both declared as being defined somewhere else, AND defined as static within the module seems like an error. Visual Studio is silent about it, and g++ is silent only in a specific order, but either way you just shouldn't have both lines in the same source code.
C++:
7) A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.
So, the first one attempts to first gives i
external linkage, and internal afterwards.
The second one gives it internal linkage first, and the second line doesn't attempt to give it external linkage because it was previously declared as internal.
8) The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however.
[ Example:
[...]
static int b; // b has internal linkage
extern int b; // b still has internal linkage
[...]
extern int d; // d has external linkage
static int d; // error: inconsistent linkage
[...]
© 2022 - 2024 — McMap. All rights reserved.