In theory, this may even be nowhere, because your program has undefined
behaviour. According to 6.9.2, all three declarations of a are also
tentative definitions, and (there being no others in their respective
translation units) end up as external definitions. But according to
6.9#5, there shall only be one of those. You have three. Hence, you
break a "shall" that is outside a constraint; hence, you have undefined
behaviour.
Right.
As I wrote: in theory. In practice, gcc probably solves this by
noticing, during its linking stage, that it has collected three a's in
its public symbol table and they all have the same type, so it creates
one object with that type for all three of those identifiers to refer
to. That's how I would do it, except that I would print a warning
message, as well.
People sometimes use gcc to mean just the compiler(s?), and
sometimes the driver which runs the whole sequence including linking.
But the linking 'stage' as you call it is always a separate program.
On some platforms/targets, the linker is from another GNU package
'binutils' and we can still make useful statements about it from the
GNU doc (although not strictly the gcc part), but on some platforms
gcc uses the 'native' linker. In particular, GNU ld is (I think
always) and other linkers _may_ be affected by whether (the compiler
actually invoked under) gcc marks items in the object files as
'common'; this can be controlled by -f[no-]common.
The traditional behavior for common is to take the largest size
specified by any object file = t.u., and (initial) contents specified
by some object file that does so if any do. I.e. if all files leave
the contents default = zero-bits, that is used; if only one file has
contents, those are used; if more than one file has contents, one of
them is used, but which one might vary depending on the linker.
AFAIK GNU ld always does this for common, and other linkers if they
have common at all are reasonably likely to do so. Object files
typically don't represent type in a fashion understood by the linker,
only size; and even size differences aren't usually treated as an
error because this capability was originally designed to handle COMMON
in FORTRAN, hence the name, which does allow some size differences.
GNU ld gives an error if multiple t.u.s have no-common definitions.
Other linkers may vary rather more in this mode.
Similar principles apply to many (probably all) other compilers also,
but the details vary.
Goodness knows where it leaves that object, though. But you should not
need to care. All you should need to know is that if you correct your
code, a has external linkage (i.e., all a's refer to the same object);
it has static duration (i.e., that object is created just before your
main function starts and isn't destroyed until after your last atexit()
function ends); and since you didn't explicitly initialise this static
object, it is automatically initialised to 0; and finally, that it is
highly likely, even if not guaranteed, that this is the case even in
your broken case.
Agree with all of those. And add that you may not know, and shouldn't
care, where the linker puts an object even when it's coded correctly:
one definition and other nondefining references; or no references.