6

fileA.cpp:

#include <iostream>
extern int iA;
extern int iB= iA;
int main()
{
std::cout<<iA<<','<<iB;
}

fileB.cpp

extern int iB;
extern int iA = 2*iB;

Compiled and linked and ran, out come in the debug and release mode is 0,0 My question is how it works, why there is no issue in linking stage? I'm using VC++2003.

Cœur
  • 37,241
  • 25
  • 195
  • 267
henryyao
  • 1,758
  • 6
  • 23
  • 37
  • gcc reports: `fileb.cpp:2: warning: 'iA' initialized and declared 'extern' extern int iA = 2*iB; ^` – PiotrNycz Jun 12 '15 at 12:51
  • @PiotrNycz No warning in VisualStudio. What gcc gave does not looks like a warning at all. I bet it would gave the same warning if i have: extern int iA = 10; – henryyao Jun 12 '15 at 12:55
  • Yes, you will have the same warning, btw - gcc also produces `0,0` – PiotrNycz Jun 12 '15 at 13:01

1 Answers1

3

The initialiser overrides the extern keyword, so there's nothing "magical" about this: you're just declaring and defining two completely unrelated variables in different translation units.

From Standard for Programming Language C++ - Chapter 3.1:

A declaration is a definition unless it declares a function without specifying the function’s body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification25 (7.5) and neither an initializer nor a function-body, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), it is a template-parameter (14.1), it is a parameter-declaration (8.3.5) in a function declarator that is not the declarator of a function-definition, or it is a typedef declaration (7.1.3), an alias-declaration (7.1.3), a using-declaration (7.3.3), a static_assert-declaration (Clause 7), an attribute-declaration (Clause 7), an empty-declaration (Clause 7), or a using-directive (7.3.4).

Your program is thus equivalent to the following:

fileA.cpp

#include <iostream>
extern int iA;
int iB= iA;
int main()
{
std::cout<<iA<<','<<iB;
}

fileB.cpp

extern int iB;
int iA = 2*iB;

Both objects necessarily undergo static initialization (to bitwise all-zeroes) before anything else happens. When dynamic-initialization later takes place, depending on whether the static-storage-duration objects in fileA.cpp or fileB.cpp get initialised first (and you can't know what order that'll be) either iB is initialized to a zero iA (then iA is initialized to 2*iB as expected), or iA is initialised to a zero iB multiplied by two, which is still zero (then iB is initialized to a zero iA).

Either way, both objects are going to end up, via well-defined semantics, having a value of zero.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    I believe this is not undefined behaviour but unspecified behaviour. As stated in `3.6.2 Initialization of non-local variables [basic.start.init]` - there are two phases of initialization - static and dynamic. static initialization in this case causes that variables are zeroed. Then dynamic initialization does not change this state. The order of dynamic initialization from different source units is unspecified - but both of possible orders give the same result. – PiotrNycz Jun 12 '15 at 13:21
  • Snuck a fix over the border – Lightness Races in Orbit Jun 12 '15 at 13:24
  • Zero initialization (which is the alternative to constant initialization in static initialization) sets a variable to value zero, which is probably all bits zero, but need not be. – Deduplicator Oct 20 '21 at 23:40