Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do c++ compilers find an extern variable?

I compile this program by g++ and clang++. There has a difference:
g++ prints 1, but clang++ prints 2.
It seems that
g++: the extern varible is defined in the shortest scope.
clang++: the extern varible is defined in the shortest global scope.

Does C++ spec has any specification about that?

main.cpp

#include <iostream>
static int i;
static int *p = &i;

int main() {
  int i;
  {
    extern int i;
    i = 1;
    *p = 2;
    std::cout << i << std::endl;
  }
}

other.cpp

int i;

version: g++: 7.4.0/ clang++:10.0.0
compilation: $(CXX) main.cpp other.cpp -o extern.exe

like image 467
eddie kuo Avatar asked Jan 07 '20 08:01

eddie kuo


People also ask

How does extern variable work in C?

“extern” keyword is used to extend the visibility of function or variable. By default the functions are visible throughout the program, there is no need to declare or define extern functions. It just increase the redundancy. Variables with “extern” keyword are only declared not defined.

Where are extern variables stored in memory in C?

extern variables are stored in the data segment.

Do you need extern in C?

the extern keyword is used to extend the visibility of variables/functions. Since functions are visible throughout the program by default, the use of extern is not needed in function declarations or definitions. Its use is implicit. When extern is used with a variable, it's only declared, not defined.

Is extern default in C?

Global variables are not extern nor static by default on C and C++.


2 Answers

[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.

like image 132
Daniel Langr Avatar answered Oct 03 '22 23:10

Daniel Langr


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.

like image 21
xmh0511 Avatar answered Oct 04 '22 00:10

xmh0511