>Mark McIntyre wrote:
Well yes, but this isn't relevant to Jason's point. All identifiers
at file scope have external linkage unless specified as static.
(There is an exception to this rule; see below.)
The extern tells the compiler to look elsewhere for a definition.
However if an initialisation is supplied, the storage class specifier
is ignored.
In article <11*************@news.supernews.com>
Andrey Tarasevich <an**************@hotmail.com> wrote:While this description might be accurate enough for practical purposes,
it still does not match the approach to "external linkage" used in the
language specification.
Yes. But the real problem here is confusion between the keyword
"extern", which is syntactically a storage-class specifier (in the
same group as "static", "register", and "typedef"), and "external
linkage", which has almost nothing to do with the keyword. In C's
tradition of misusing the English language, :-) the word "extern"
is not required to mean "external linkage", so it does not. The
"const" keyword does not define constants, "typedef" does not define
new types, and "extern" does not mean external linkage. (Similarly,
"static" sometimes means "static storage" and sometimes means
something else entirely, as will appear below.)
'extern' specifier explicitly describes an identifier as having external
linkage. This is as true in cases when 'extern' is a part of a
non-defining declaration, as it is true in cases when 'extern' is a part
of a definition. When applied to a declaration, it indeed means that the
compiler has to look for a definition elsewhere. When applied to a
definition, its meaning is essentially reversed: it means that this
definition can be looked up from some other place.
I am not sure what you mean to say with that last sentence. I think
the only way to fully describe the "extern" keyword is to exhaustively
enumerate all the ways in which it can be used with a variable-name.
These are:
/* all outside a function */
int a; /* not extern */
extern int b;
int d = 0;
extern int e = 0;
static int g;
extern int g;
int g;
int g = 42;
void func(void) {
extern int h;
int i;
static int j;
}
Here "a" is both declared and defined, because we omitted the
extern keyword. We have no initializer so this is a "tentative
definition" -- we can later write "int a = 3" to set a to 3
instead of the default 0, but by the end of the translation
unit, the variable will be defined.
The variable "b", on the other hand, is only declared. The
extern keyword suppresses the tentative definition. This is
its only real function when applied to file-scope variables:
it only ever suppresses definitions, and only when they would
have been tentative anyway (as we will see with "e").
Next we have d and e, which are declared-and-defined and both
explicitly initialized. These are not tentative definitions
so the "extern" keyword has no effect -- it can only suppress
tentative definitions.
Now we have g. This is one of the more peculiar cases. By first
declaring it "static", and not using an initializer, we create a
tentative definition and inhibit the default linkage (external
linkage) -- we wind up with a file-scope g with internal linkage,
which will be initialized to 0 if we do not override this. The
second declaration uses the "extern" keyword. In this case, the
keyword *also* means "static", because we already have a definition
of the same identifier at the same scope. When the identifier is
already defined, "extern" means "whatever linkage it had before".
The third declaration for "g" *also* means "static": the absence
of the "extern" keyword fails to suppress tentative-definition-ness,
but has no effect on linkage, because the default linkage behavior
is to act as if the "extern" keyword had been present. Finally,
the fourth line for "g" is a real, solid, actual definition -- not
tentative -- and gives it the value 4. As before, the nonexistent
extern keyword has no effect on linkage: the linkage is the same
as if we had used the extern keyword, and is still "static". All
four declarations mean the same thing, but only the fourth is
a non-tentative definition.
The "extern" keyword does sometimes means something more than
"suppress tentative definition", though, and this appears inside
the function func(). The variable h is declared (but not
defined) as having external linkage. Its scope is limited;
the declaration disappears after the "}" that ends the function.
The variable i is both declared and defined, and has automatic
storage duration; and the variable j is also both declared and
defined, and has static storage duration.
Note that all file-scope variables always have static duration:
a, b, d, e, and g are all static-duration variables. Like the
"extern" keyword, the "static" keyword would be redundant; so, like
the "extern" keyword, the "static" keyword is given a new and
unrelated meaning: it alters the identifier's linkage. The
"non-static" variables (a, b, d, and e) have external linkage, but
the "static" variable (g) has internal linkage.
We can now summarize this:
extern:
If a variable would have had no linkage (and thus automatic
storage duration), this keyword gives it linkage, usually
external, but sometimes internal. If the variable would
already have had external or internal linkage, the "extern"
keyword has no effect on linkage; instead, it suppresses
tentative definitions, but not actual definitions.
static:
If a variable would have had automatic storage duration
(and thus no linkage), this keyword gives it static storage
duration instead. Otherwise, it gives the variable internal
linkage.
These two keywords can also be applied to function-names, but since
functions never have storage duration to worry about, the situation
there is simpler: "extern" has no effect, and "static" only affects
linkage.
There is one situation I deliberately glossed over. It is possible
to give the same identifier both internal and external linkage in
the same translation unit. We do this by hiding the internal-linkage
name with a function-scope name, then using the "extern" keyword:
static int x; /* internal linkage, file scope, static duration */
void f(void) {
int x; /* hides the internal-linkage x */
{
extern int x; /* BAD - DO NOT DO THIS */
}
}
The C Standards (C89 and C99 both) say that the behavior here is
undefined. We simply do not know what will happen -- will we
get this translation unit's x, or some other translation unit's
x, or will things just go kaboom? It is probably wisest not to
find out the hard way. :-)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.