This difference in behavior is not a coincidence, nor a bug in the compilers. It's the strict application of the C and the C++ standards, which diverge on the meaning of having several int i;
in the global scope.
In C++ it is invalid
In C++, int i;
is a definition of an (uninitialized) object. The One Definition Rule (ODR) does not allow you to define several time the same global variable.
This is defined in the C++ standard, in section [basic.def.odr]
In C it is valid
In C, int i;
is a tentative definition. It is perfectly valid to have several tentative declaration of exactly the same global variable.
This is defined in the C11 standard, section 6.9.2 External object definitions:
/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.
Note that this clause is worded in a way that doesn't say anything about the case where the same variable is defined in several translation units. The last sentence of the standard quote above does not mean that it's a different variable in each file (for this you'd need internal linkage, with static
). It just says that the behavior is as if the initial value of the variable would be 0.
This neutrality has a reason:
The standard identifies the case as undedefined behavior:
Annex J.2: An identifier with external linkage is used, but in the program there
does not exist exactly one external definition for the identifier, or
the identifier is not used and there exist multiple external
definitions for the identifier
But the standard also identifies the case with multiple definitions as a common extension that is widely supported, as long as these definitions do not contradict each other:
Annex J.5.11: There may be more than one external definition for the identifier of an object, with or without the explicit use of the
keyword extern
; if the definitions disagree, or more than one is
initialized, the behavior is undefined
Important advice
For this reason, if you intend to write portable code, I recommend strongly to use extern
in the header and define the value in one and only one of the compilation unit. This is safe, clear, unambiguous, and works in C as well as C++.