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.