Mark said:
(of two files with int a; in them)
The same was my understanding until this thread, so perhaps I can
explain it knowing where you're coming from, because actually it
behaves as if it had been declared extern int a = 0;
Really? The standard makes it pretty clear that in the absence of a
storage class specifier, a file-scope object is considered to be
extern.
You are probably referring to 6.2.2#5. As Chris Torek points out, this
section specifies that the extern keyword gives the object external
linkage. Aside from linkage, there are two other relevant contexts in
which the word "external" can be applied: to a declaration and to a
definition.
And FWIW I can't recall a compiler that behaves otherwise than
to consider two such declarations to be synonyms. I confess, my
initial thought was the same as yours, but closer reading of the
standard deconvinced me. Can you point me to the actual section that
confirms your view?
You seem to already know the part of the standard requiring that int a;
is treated as extern int a;
6.2.2#5
....
If the declaration of an identifier for
an object has file scope and no storage-class specifier, its
linkage is external.
Below an external declaration is defined as a file-scope declaration,
where it is also clarified that an initialiser turns a declaration into
a definition:
6.9#4
As discussed in 5.1.1.1, the unit of program text after
preprocessing is a translation unit, which consists of a
sequence of external declarations. These are described as
``external'' because they appear outside any function (and
hence have file scope). As discussed in 6.7, a declaration
that also causes storage to be reserved for an object or a
function named by the identifier is a definition.
Below we find out that int a; becomes int a = 0; by end of file in the
absence of other definitions. i.e. 6.2.2#5, 6.9#4 and 6.9.2#2 require
that our external declaration int a; by file end becomes the external
definition with external linkage: extern int a = 0.
6.9.2#2
[#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.
Below we find that a program shall only have one external definition
for each identifier that is actually used. Given that our two files
each contain an identical variable declaration that ultimately becomes
an external definition, this constraint is violated (unless the
variable is not actually used).
6.9#5
An external definition is an external declaration that
is also a definition of a function or an object. If an
identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof
operator), somewhere in the entire program there shall be
exactly one external definition for the identifier;
otherwise, there shall be no more than one.127)
____________________
127Thus, if an identifier declared with external linkage is
not used in an expression, there need be no external
definition for it.
Quite a chain of reasoning for the simple conclusion: we must use
extern for one of the declarations.