Search

6.7 — External linkage

In the prior lesson (6.6 -- Internal linkage), we discussed how internal linkage limits the use of an identifier to a single file. In this lesson, we’ll explore the concept of external linkage.

An identifier with external linkage can be seen and used both from the file in which it is defined, and from other code files (via a forward declaration). In this sense, identifiers with external linkage are truly “global” in that they can be used anywhere in your program!

Functions have external linkage by default

In lesson 2.8 -- Programs with multiple code files, you learned that you can call a function defined in one file from another file. This is because functions have external linkage by default.

In order to call a function defined in another file, you must place a forward declaration for the function in any other files wishing to use the function. The forward declaration tells the compiler about the existence of the function, and the linker connects the function calls to the actual function definition.

Here’s an example:

a.cpp:

main.cpp:

The above program prints:

Hi!

In the above example, the forward declaration of function sayHi() in main.cpp allows main.cpp to access the sayHi() function defined in a.cpp. The forward declaration satisfies the compiler, and the linker is able to link the function call to the function definition.

If function sayHi() had internal linkage instead, the linker would not be able to connect the function call to the function definition, and a linker error would result.

Global variables with external linkage

Global variables with external linkage are sometimes called external variables. To make a global variable external (and thus accessible by other files), we can use the extern keyword to do so:

Non-const global variables are external by default (if used, the extern keyword will be ignored).

Variable forward declarations via the extern keyword

To actually use an external global variable that has been defined in another file, you also must place a forward declaration for the global variable in any other files wishing to use the variable. For variables, creating a forward declaration is also done via the extern keyword (with no initialization value).

Here is an example of using a variable forward declaration:

a.cpp:

main.cpp:

In the above example, a.cpp and main.cpp both reference the same global variable named g_x. So even though g_x is defined and initialized in a.cpp, we are able to use its value in main.cpp via the forward declaration of g_x.

Note that the extern keyword has different meanings in different contexts. In some contexts, extern means “give this variable external linkage”. In other contexts, extern means “this is a forward declaration for an external variable that is defined somewhere else”. Yes, this is confusing, so we summarize all of these usages in lesson 6.11 -- Scope, duration, and linkage summary.

Warning

If you want to define an uninitialized non-const global variable, do not use the extern keyword, otherwise C++ will think you’re trying to make a forward declaration for the variable.

Warning

Although constexpr variables can be given external linkage via the extern keyword, they can not be forward declared, so there is no value in giving them external linkage.

Note that function forward declarations don’t need the extern keyword -- the compiler is able to tell whether you’re defining a new function or making a forward declaration based on whether you supply a function body or not. Variables forward declarations do need the extern keyword to help differentiate variables definitions from variable forward declarations (they look otherwise identical):

File scope vs. global scope

The terms “file scope” and “global scope” tend to cause confusion, and this is partly due to the way they are informally used. Technically, in C++, all global variables have “file scope”, and the linkage property controls whether they can be used in other files or not.

Consider the following program:

global.cpp:

main.cpp:

Variable g_x has file scope within global.cpp -- it can be used from the point of definition to the end of the file, but it can not be directly seen outside of global.cpp.

Inside main.cpp, the forward declaration of g_x also has file scope -- it can be used from the point of declaration to the end of the file.

However, informally, the term “file scope” is more often applied to global variables with internal linkage, and “global scope” to global variables with external linkage (since they can be used across the whole program, with the appropriate forward declarations).

The initialization order problem of global variables

Initialization of global variables happens as part of program startup, before execution of the main function. This proceeds in two phases.

The first phase is called static initialization. In the static initialization phase, global variables with constexpr initializers (including literals) are initialized to those values. Also, global variables without initializers are zero-initialized.

The second phase is called dynamic initialization. This phase is more complex and nuanced, but the gist of it is that global variables with non-constexpr initializers are initialized.

Here’s an example of a non-constexpr initializer:

Within a single file, global variables are generally initialized in order of definition (there are a few exceptions to this rule). Given this, you need to be careful not to have variables dependent on the initialization value of other variables that won’t be initialized until later. For example:

This prints:

0 5

Much more of a problem, the order of initialization across different files is not defined. Given two files, a.cpp and b.cpp, either could have its global variables initialized first. This means that if the variables in a.cpp are dependent upon the values in b.cpp, there’s a 50% chance that those variables won’t be initialized yet.

Warning

Dynamic initialization of global variables causes a lot of problems in C++. Avoid it whenever possible.

Quick summary

We provide a comprehensive summary in lesson 6.11 -- Scope, duration, and linkage summary.

Quiz time

Question #1


What’s the difference between a variable’s scope, duration, and linkage? What kind of scope, duration, and linkage do global variables have?

Show Solution


6.8 -- Global constants and inline variables
Index
6.6 -- Internal linkage

18 comments to 6.7 — External linkage

  • jiik

    Hi, I'm having some difficulties understanding the "constexpr" specifier in general.

    In a warning box of this lesson I can read:

    "Although constexpr variables can be given external linkage via the extern keyword, they can not be forward declared, so there is no value in giving them external linkage."

    Doesn't this conflicts with the code that is written in the Quick summary section?

  • vie_man

    I found a small typo:
    "Technically, in C++, all global variables in C++ have “file scope”, ..." The second "C++" is redundant.

  • Jimmy Hunter

    I found another small typo you might want to correct for improved readability.
    In section "The initialization order problem of global variables", 3rd paragraph,
    You wrote "This phase is is more complex...".
    Should be "This phase is more complex..."

  • luisizq

    In the section "Variable forward declarations via the extern keyword" I think there is an error in an example

    Instead of:

    // non-constant
    int g_x; // variable definition (can have initializer if desired)
    extern int g_x; // forward declaration (no initializer)

    // constant
    extern const g_y { 1 }; // variable definition (const requires initializers)
    extern const g_y; // forward declaration (no initializer)

    There should be

    // non-constant
    int g_x; // variable definition (can have initializer if desired)
    extern int g_x; // forward declaration (no initializer)

    // constant
    extern const int g_y { 1 }; // variable definition (const requires initializers)
    extern const int g_y; // forward declaration (no initializer)

  • Jojo

    Hello,
    I am confused by title "The static initialization order problem": While reading the following content, I thought more about a title like "The problem while initializing static variables". Indeed, the problem concerns the dynamic initialization of static variables rather than their static initialization?

    • nascardriver

      Good catch!
      static initialization isn't the problem, dynamic initialization is. Title updated to "The initialization order problem of global variables".

  • Tim

    Been learning cpp now for around 4 years, mostly thanks to this site I think I am reasonably competent now, but I still find it incredibly useful reference material for stuff that I do not do very often.

    I have started using a global stream for my logs as passing the object to every single class just seemed needlessly messy and the built in streams could not be used because I a writing a daemon, but I was getting linker errors as I had not forward declared my variable where I initialised it in main.  I still have it declared extern in my header file though, it seems to work, but is that wrong?

  • Armitage

    I'm confused about "static variables" term in section "The static initialization order problem".

    g_x— is a static variable?

    • Alex

      In this context, "static variable" means a variable with static duration. Since all globals have static duration, g_x is indeed a static variable.

  • Thomas

    At the beginning of this lesson, it says : "In the prior lesson (8.5b -- Non-static member initialization)". Just to let you know, if you want to put the correct one (6.6 — Internal linkage).

Leave a Comment

Put all code inside code tags: [code]your code here[/code]