Can a variable be declared both static and extern?
Asked Answered
H

4

24

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?

Huge answered 18/1, 2013 at 16:58 Comment(3)
Which language are you asking about, C or C++? If you want correct answers for both, please say so. Otherwise please delete one of the language tags.Interrupted
@Rob Yes, I want correct answer for both. I would be surprised if C and C++ behave differently on this one, wouldn't you? AndHuge
@arrows I wouldn't be surprised at all.Hoenack
T
20

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).

Thumbnail answered 18/1, 2013 at 17:17 Comment(2)
It's actually in section 7.1.1.8Hoenack
"Section 3.5.6 discusses how extern should behave in this case." 3.5/6 is only about block-scope names. I cannot find any similar specification about namespace-scope names.Waits
C
11

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.

Ciapas answered 18/1, 2013 at 17:22 Comment(0)
P
1

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.

Provisional answered 18/1, 2013 at 17:19 Comment(2)
I'm not sure which compiler is "correct". Noone in this world has ever lost money by betting that MSVC is wrong when it disagrees with G++ ;)Wiry
Both compilers are correct in this case. The first example is UB, so the compiler is not required to issue a diagnostic. The second example is standard-compliant.Ciapas
H
1

C++:

7.1.1 Storage class specifiers [dcl.stc]

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
[...]
Hoenack answered 18/1, 2013 at 17:20 Comment(2)
is global also a namespace scope?Huge
@arrows yes - the global namespace.Hoenack

© 2022 - 2024 — McMap. All rights reserved.