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.7 -- 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:
1 2 3 4 5 6 |
#include <iostream> void sayHi() // this function has external linkage, and can be seen by other files { std::cout << "Hi!"; } |
main.cpp:
1 2 3 4 5 6 7 8 |
void sayHi(); // forward declaration for function sayHi, makes sayHi accessible in this file int main() { sayHi(); // call to function defined in another file, linker will connect this call to the function definition return 0; } |
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:
1 2 3 4 5 6 7 8 9 |
int g_x { 2 }; // non-constant globals are external by default extern const int g_y { 3 }; // const globals can be defined as extern, making them external extern constexpr int g_z { 3 }; // constexpr globals can be defined as extern, making them external (but this is useless, see the note in the next section) int main() { return 0; } |
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:
1 2 3 |
// global variable definitions int g_x { 2 }; // non-constant globals have external linkage by default extern const int g_y { 3 }; // this extern gives g_y external linkage |
main.cpp:
1 2 3 4 5 6 7 8 9 10 11 |
#include <iostream> extern int g_x; // this extern is a forward declaration of a variable named g_x that is defined somewhere else extern const int g_y; // this extern is a forward declaration of a const variable named g_y that is defined somewhere else int main() { std::cout << g_x; // prints 2 return 0; } |
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):
1 2 3 4 5 6 7 |
// 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) |
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:
1 2 |
int g_x { 2 }; // external linkage by default // g_x goes out of scope here |
main.cpp:
1 2 3 4 5 6 7 8 9 |
extern int g_x; // forward declaration for g_x -- g_x can be used beyond this point in this file int main() { std::cout << g_x; // should print 2 return 0; } // the forward declaration for g_x goes out of scope here |
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:
1 2 3 4 5 6 |
int init() { return 5; } int g_something{ init() }; // non-constexpr initialization |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> int initx(); // forward declaration int inity(); // forward declaration int g_x{ initx() }; // g_x is initialized first int g_y{ inity() }; int initx() { return g_y; // g_y isn't initialized when this is called } int inity() { return 5; } int main() { std::cout << g_x << ' ' << g_y << '\n'; } |
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
1 2 3 4 5 6 7 8 9 |
// External global variable definitions: int g_x; // defines non-initialized external global variable (zero initialized by default) extern const int g_x{ 1 }; // defines initialized const external global variable extern constexpr int g_x{ 2 }; // defines initialized constexpr external global variable // Forward declarations extern int g_y; // forward declaration for non-constant global variable extern const int g_y; // forward declaration for const global variable extern constexpr int g_y; // not allowed: constexpr variables can't be forward declared |
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?
![]() |
![]() |
![]() |
Why can constexpr variables not be forward declared?
"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."
so what if we have a constant variable in add.cpp that is a compile-time constant and so defined with constexpr but is needed in main.cpp ?
Move it to a header file and #include it wherever you need it.
Why can't constexpr variables be forward declared?
Because the compiler needs to know the value of a constexpr variable. A forward declaration doesn't provide this. The linker could figure this out, but that doesn't run until after the compiler.
Oooh, thanks for that insight!
We can't use the global const variables with external linkage in another file if we can't declare them forward in files where we want to use them. Even though we can assign the external linkage to contsexpr global variables we can't use such constexpr variables in another files so it seems that giving them external linkage is pretty pointless or useless. And I expected compiler will complain when we give constexpr variables external linkage but the compiler didn't.
Please illuminate me with some cases where giving external linkage to constexpr variables is useful.
After further digging I think I found a way to use constexpr variables with external linkage in another file without taking the help from header file or preprocessing directive. But I am not sure if it is the good practise or my code is buggy. Here it is:
link.cpp:
main.cpp:
and when I run this code the outputs are as the following:
see? We can use the g_z variable which is constexpr along with external linkage in main.cpp. I forward declared the g_z with
and
keyword in main.cpp. Is this the good practise? or it is one of the undefined behaviors?
iirc this is well-defined, but now main's `g_z` is not usable in constant expressions. If you want a variable to be `constexpr`, its definition has to be visible (ie. defined in a header or in the source file that uses it).
Therefore my understandings of global variable with constexpr are: [Please correct me If I am wrong]
01. Global variables with constexpr have only internal linkage
02. And they are only avaiable within the file scope.(using Header file might give us option to use them somewhere.)
03. Using 'static' and 'extern' keyword with Global variables with constexpr are totally pointless.
This unit of lesson shows the glimpse of complexity in C++, though I like the complexity which gives the programmers freedom.It also shows the beauty and flexibility of C++. I think most programmers don't like C++ because it has too many rules and it gives programmers absolute freedom, though we like freedom in personal our personal life but not in programming world.
output:99
hi . i run this program on visual studio
why output is 99 ?
must be 09
The crucial idea in this code is function B(); because it is the one that has a literal value (9).
BB variable is initialized with B() function which is 9.
AA variable is initialized with A() function which uses BB variable which is 9.
So AA variable is nothing but BB variable which is 9.
hope it helps...
I don't know the reason why @winterson is getting "99", but I wanna join in because I think you're wrong.
`AA` and `BB` are zero-initialized during static initialization (Because they're not initialized with literals). During dynamic initialization, global variables are initialized in the order of their definition. That is, `AA` is initialized first with the value of `BB`, which is still 0 at this point.
I suspect @winterson's compiler is initializing `AA` and `BB` at compile-time already, which breaks the initialization order (I don't think this is allowed to happen).
Either way, global variables are evil and should be avoided, this is a good example.
Sorry for the mess!!
Thank you very much for correction.
Since constexpr can't be forward declared does that mean we can never use it everywhere in our program? Is it only limited to the file it is initialized in?
Correct. But `constexpr` variables have internal linkage, so you can define them in headers.
a way from 'constexpr' case .. which is better for multi-files-variable-access, to define variables in a header, or to use 'extern' global variables? Is there any conditions of using either?
If the variable can be `constexpr`, make it `constexpr`. It's likely faster and safer.
Thank you for your time.
You gave a choice of defining a constexpr variable in a header file.
I was wondering, about not to use the 'external linkage 'and just stick to headers!
The next lesson has cleared anything..Thanx.
I am a little confused. As far as I understand:
Static initialization include the following:
Dynamic initialization include the following:
Am I right?
All of your examples are already initialized during static initialization, because you're initializing the variables with literals.
The first snippet in section "The initialization order problem of global variables" shows when dynamic initialization is needed. The compiler doesn't know what `init` will return (In practice, compilers are smart enough to figure it out, but let's assume a dumb compiler), so `g_something` is zero-initialized during static initialization and gets the correct value (5) during dynamic initialization.
extern const int g_y { 3 }; // const globals can BE defined as extern, making them external
I think "be" was missing!
thanks!
"...and the linker connects the function calls to the actual function definition."
Does this happen when program is running in the memory? or does this happen when a program is processed via the linker?
Both answers are valid. If your compiling only your code, or are only using static libraries, the linker can connect everything right after compilation. If you use shared libraries, functions will be connected when you launch the program.
Thank you for the lesson and all your hard work!
In the last part you mentioned "Given two files, a.cpp and b.cpp, either could have its global variables initialized first". Is there any way to set the order of compilation, such as compiling a.cpp first and the b.cpp?
No. Even if your compiler has a way of specifying the compilation order, it's not standardized and your code will break on another system.
Hello,I've question look at the code below:
in exp.cpp
in main.cpp:
I've stated my question in the comment. could you pls tell me why can't I assign it in global scope but can do it in main function ? thanks
Why can't constexpr variables be forward declared? Is there a reason behind this restriction? Also, how does one remember which one (among const and constexpr) is allowed and which is not? Seems kind of arbitrary.
> Why can't constexpr variables be forward declared? Is there a reason behind this restriction?
I don't know the reason honestly. My guess is that, because it's not necessary to forward declare constexpr variables (ie. code can be rewritten without the forward declaration), it's not allowed, so that it's easier for the compiler to evaluate constant expressions.
> Also, how does one remember which one (among const and constexpr) is allowed and which is not?
You can use `constexpr` for constant expressions, ie. literals and entities marked as `constexpr`. `constexpr` functions and types aren't covered on learncpp, so we're limited to literals and other constexpr variables.
In the initial explanation of constexpr is was said that "To help provide more specificity, C++11 introduced new keyword constexpr, which ensures that a constant must be a compile-time constant:"
Forward declarations are somewhat incompatible with that notion, since they are dealt with _after_ compilation (at the linkage stage).
P.S. That quote should say 'introduced the keyword constexpr'. It's section 4.13
P.S.S. From this page "Variables forward declarations do need the extern keyword to help differentiate variables definitions from variable forward declarations (they look otherwise identical):" -> "Variable forward declarations do need the extern keyword to help differentiate variable definitions from variable forward declarations (they look otherwise identical):"
Thanks for pointing out the typo, I fixed it.
My first thought was, yeah, that makes perfect sense. Then again, we do have forward declarations for constexpr functions (Not covered on learncpp).
Found a typo. Line 4 of the quick summary has an open parentheses at the end of the comment. Possibly an unfinished thought or just a typo?
Whatever it was, it's gone now. Thanks!
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?
No conflict there.
is allowed, but not useful.
I found a small typo:
"Technically, in C++, all global variables in C++ have “file scope”, ..." The second "C++" is redundant.
"This phase is is more complex and nuanced, ..." another one.
Fixed. Thanks!
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..."
Thanks! This issue has has been fixed.
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)
Yep, thanks!
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?
Good catch!
static initialization isn't the problem, dynamic initialization is. Title updated to "The initialization order problem of global variables".
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?
Declare it `extern` in the header, define it once in a single source fail.
Thanks for the confirmation.
I'm confused about "static variables" term in section "The static initialization order problem".
g_x— is a static variable?
In this context, "static variable" means a variable with static duration. Since all globals have static duration, g_x is indeed a static variable.
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).
Link updated, thanks!