Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Sharing Variables with External Linkage
Share variables across multiple files using extern declarations.
External Linkage and Variable Forward Declarations
In the prior lesson on 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.
Identifiers with external linkage are visible to the linker. This allows the linker to do two things:
- Connect an identifier used in one translation unit with the appropriate definition in another translation unit.
- Deduplicate inline identifiers so one canonical definition remains. We discuss inline variables and functions in the Inline functions and variables lesson.
Functions have external linkage by default
In the Programs with multiple code files lesson, 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:
greetings.cpp:
#include <iostream>
void greet() // this function has external linkage, and can be seen by other files
{
std::cout << "Hello there!\n";
}
main.cpp:
void greet(); // forward declaration for function greet, makes greet accessible in this file
int main()
{
greet(); // call to function defined in another file, linker will connect this call to the function definition
return 0;
}
The above program prints:
Hello there!
In the above example, the forward declaration of function greet() in main.cpp allows main.cpp to access the greet() function defined in greetings.cpp. The forward declaration satisfies the compiler, and the linker is able to link the function call to the function definition.
If function greet() 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:
int g_counter{10}; // non-constant globals are external by default (no need to use extern)
extern const int g_maxSize{100}; // const globals can be defined as extern, making them external
extern constexpr int g_bufferSize{512}; // constexpr globals can be defined as extern, making them external (but this is pretty useless, see the warning in the next section)
int main()
{
return 0;
}
Non-const global variables are external by default, so we don't need to mark them as extern.
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 variable forward declarations:
main.cpp:
#include <iostream>
extern int g_counter; // this extern is a forward declaration of a variable named g_counter that is defined somewhere else
extern const int g_maxSize; // this extern is a forward declaration of a const variable named g_maxSize that is defined somewhere else
int main()
{
std::cout << g_counter << ' ' << g_maxSize << '\n'; // prints 10 100
return 0;
}
Here's the definitions for those variables:
globals.cpp:
// global variable definitions
int g_counter{10}; // non-constant globals have external linkage by default
extern const int g_maxSize{100}; // this extern gives g_maxSize external linkage
In the above example, globals.cpp and main.cpp both reference the same global variable named g_counter. So even though g_counter is defined and initialized in globals.cpp, we are able to use its value in main.cpp via the forward declaration of g_counter.
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 the Scope, duration, and linkage summary lesson.
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.
Although constexpr variables can be given external linkage via the `extern` keyword, they can not be forward declared as constexpr. This is because the compiler needs to know the value of the constexpr variable (at compile time). If that value is defined in some other file, the compiler has no visibility on what value was defined in that other file.
However, you can forward declare a constexpr variable as const, which the compiler will treat as a runtime const. This isn't particularly useful.
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 uninitialized variables definitions from variable forward declarations (they look otherwise identical):
// constant extern const int g_maxSize{100}; // variable definition (const requires initializers) extern const int g_maxSize; // forward declaration (no initializer)
## Avoid using `extern` on a non-const global variable with an initializer
The following two lines are semantically equivalent:
```cpp
int g_counter{10}; // extern by default
extern int g_counter{10}; // explicitly extern (may cause compiler warning)
However, your compiler may issue a warning about the latter statement, even though it is technically valid.
Remember when we said compilers have the leeway to issue a diagnostic for things they find suspicious? This is one of those cases. Conventionally, extern is applied to a non-const variable when we want a forward declaration. However, adding an initializer makes the statement a definition instead. The compiler is telling you that something seems amiss. To correct it, either remove the initializer (if you intended a forward declaration) or remove the extern (if you intended a definition).
Only use `extern` for global variable forward declarations or const global variable definitions. Do not use `extern` for non-const global variable definitions (they are implicitly `extern`).
External linkage: An identifier with external linkage can be seen and used both from the file where it's defined and from other files via forward declarations. Identifiers with external linkage are truly "global" across the entire program.
Functions and external linkage: Functions have external linkage by default, allowing them to be called from other files using forward declarations.
External variables: Global variables with external linkage. Non-const global variables have external linkage by default. Const and constexpr variables need the extern keyword to have external linkage.
Variable forward declarations: Use the extern keyword without an initializer to create a forward declaration for a variable defined in another file. This tells the compiler the variable exists elsewhere.
The extern keyword: Has different meanings in different contexts. With an initializer, it gives a variable external linkage. Without an initializer, it creates a forward declaration.
Constexpr limitations: Constexpr variables cannot be forward declared as constexpr because the compiler needs to know the value at compile time. They can be forward declared as const (treating them as runtime const), but this isn't particularly useful.
Best practices: Only use extern for global variable forward declarations or const global variable definitions. Do not use extern for non-const global variable definitions (they're implicitly extern already).
External linkage enables code organization across multiple files while maintaining access to shared data and functions. The linker connects forward declarations with their definitions, ensuring the program works as a cohesive whole despite being split across translation units.
Sharing Variables with External Linkage - Quiz
Test your understanding of the lesson.
Practice Exercises
External Linkage with extern
Understand how extern keyword is used for forward declarations of variables with external linkage.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!