How do c++ compilers find an extern variable?

Clang gives a right result. Even though per the rule of the current standard, the program shouldn't be ill-formed. Note the emphasized wording:

If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed.

The entity declared at #3 and the entity declared at #1 are not the same entity, because of the below rule:

Two names that are the same and that are declared in different scopes shall denote the same variable, function, type, template or namespace if

  • both names have external linkage or else both names have internal linkage and are declared in the same translation unit; and
  • [...]

They're not, one has internal linkage and the other has external linkage, so they do not denote the same entity, hence the code does not violate the [basic.link#6]. Moreover, the example follows [basic.link#6] remains a wrong interpretation about variable i.

P1787 has clarified this example. It says:

static void f();
extern "C" void h();
static int i = 0;               // #1
void gq() {
  extern void f();              // internal linkage
  extern void g();              // ::g, external linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: <del>ex</del>internal linkage
  }
}

Without Even though the declaration at line #2 hides the declaration at line #1, the declaration at line #3 would link with thestill redeclarationes at line #1. Because the declaration with and receives internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed.

That means, In your example, the variable i introduced by declaration extern int i will link with the variable i declared by static int i. So, print 2 is the right behavior.


[basic.link/7] should be the relevant part of the Standard. In the current draft, it says:

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If such a declaration is attached to a named module, the program is ill-formed. If there is a visible declaration of an entity with linkage, ignoring entities declared outside the innermost enclosing namespace scope, such that the block scope declaration would be a (possibly ill-formed) redeclaration if the two declarations appeared in the same declarative region, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed.

Note that the subsequent example almost exactly matches your case:

static void f();
extern "C" void h();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: external linkage, ill-formed
  }
}

So, the program should be ill-formed. The explanation is below the example:

Without the declaration at line #2, the declaration at line #3 would link with the declaration at line #1. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed.