Question
In C++, what is the practical purpose of an extern declaration such as the following?
extern int x;
extern float y;
I understand that these are declarations without definitions, but when is extern useful in a real program? Please provide an example.
Short Answer
You will learn how extern lets one C++ source file refer to a global variable or function that is defined in another source file. You will also see the usual header/source-file organization, how the linker connects declarations to definitions, and how to avoid duplicate-definition errors.
Concept
extern declares that a name exists somewhere else in the program. It tells the compiler:
“This variable is defined in another translation unit; allow this file to use it.”
A declaration introduces a name and its type. A definition creates the variable's storage (or provides a function body).
extern int score; // Declaration: score exists elsewhere.
int score = 0; // Definition: creates the one global score variable.
This matters because C++ programs are commonly split into multiple .cpp files. Each file is compiled separately. When compiling a file that uses score, the compiler needs to know score's type, but it does not need the storage to be in that same file. During the link step, the linker finds the single definition and connects all uses to it.
For ordinary functions, an extern declaration is usually unnecessary because functions have external linkage by default:
int add(int a, int b); // A function declaration; effectively external by default.
extern is most visibly useful for global variables shared across source files.
Mental Model
Think of a declaration as an entry in a building directory:
extern int score;
It says that a room called score exists and that it contains an int, but it does not build the room.
The definition is the actual room:
int score = 0;
Many people can read the directory and visit the same room, but the building should contain only one actual room with that identity. Similarly, many source files may declare a global variable with extern, but exactly one source file should define it.
Syntax and Examples
The usual pattern is:
- Put the
externdeclaration in a header file. - Include that header wherever the variable is needed.
- Put one definition in exactly one
.cppfile.
settings.h
#ifndef SETTINGS_H
#define SETTINGS_H
extern int maxRetries;
#endif
settings.cpp
#include "settings.h"
int maxRetries = 3;
main.cpp
#include <iostream>
#include "settings.h"
int main() {
std::cout << "Retries: " << maxRetries << '\n';
maxRetries = 5;
}
main.cpp can use maxRetries because the header tells the compiler it is an int defined elsewhere. settings.cpp provides the one definition that allocates storage and initializes it.
You can also write a declaration directly in a .cpp file:
extern int maxRetries;
However, using a shared header is safer because all files receive the same declaration.
Step by Step Execution
Consider these files.
counter.h
#ifndef COUNTER_H
#define COUNTER_H
extern int requestCount;
#endif
counter.cpp
#include "counter.h"
int requestCount = 0;
main.cpp
#include <iostream>
#include "counter.h"
int main() {
++requestCount;
std::cout << requestCount << '\n';
}
Step by step:
- The compiler processes
main.cppand includescounter.h. - It sees
extern int requestCount;, so it knowsrequestCountis anintbut does not allocate storage there. ++requestCountis valid because its type is known.- The compiler processes
counter.cppseparately. - It sees
int requestCount = 0;, which creates the actual variable and initializes it to0. - The linker combines both compiled files. It resolves the use in
main.cppto the definition incounter.cpp. - At runtime,
requestCountstarts at0, is incremented to1, and the program prints:
1
Both files refer to the same variable, not separate copies.
Real World Use Cases
extern can be useful when a program intentionally has a small amount of shared global state.
- Application configuration: A configuration value is loaded once and read by several modules.
- Embedded systems: Hardware registers or memory-mapped addresses may be declared in a header and defined by a platform-specific module.
- Legacy C or C++ code: Older projects may use shared global counters, flags, or handles.
- Interoperating with another library: A library may expose a globally defined object that your code declares and uses.
- Build information: A generated source file can define a build number or version string that other files read.
For example, a program might expose a read-only version label:
// version.h
extern const char* const appVersion;
// version.cpp
const char* const appVersion = "2.4.0";
In modern C++, shared mutable globals should be used sparingly because they create hidden dependencies and make testing harder.
Real Codebase Usage
In real codebases, extern is usually part of a header-and-implementation pattern rather than something repeated manually in every .cpp file.
Centralized configuration
// config.h
#pragma once
extern int timeoutSeconds;
// config.cpp
#include "config.h"
int timeoutSeconds = 30;
Every file includes config.h rather than copying extern int timeoutSeconds;. This prevents mismatched types and makes refactoring safer.
Prefer access functions for mutable state
Instead of exposing a writable global variable, projects often expose a function:
// logger.h
#pragma once
void setLoggingEnabled(bool enabled);
bool isLoggingEnabled();
The implementation can keep the state private:
// logger.cpp
#include "logger.h"
namespace {
bool loggingEnabled = false;
}
void setLoggingEnabled(bool enabled) {
loggingEnabled = enabled;
}
bool isLoggingEnabled() {
return loggingEnabled;
}
This allows validation, synchronization, and later implementation changes without modifying every caller.
extern "C" for C library interfaces
You may also encounter extern "C", which is different from extern int x. It requests C language linkage, commonly when calling C libraries from C++:
extern "C" int puts(const char* text);
In practice, include the library's official header instead of manually declaring its functions.
Common Mistakes
Defining a variable in a header accidentally
This header is incorrect if included by multiple .cpp files:
// Bad: globals.h
int score = 0;
Each source file that includes it creates a definition of score. The linker will usually report a multiple-definition error.
Use an extern declaration in the header and one definition in a source file:
// globals.h
extern int score;
// globals.cpp
int score = 0;
Forgetting the definition
This compiles but will fail during linking if no .cpp file defines score:
extern int score;
int main() {
return score;
}
Fix it by providing exactly one definition:
int score = 0;
Giving declarations different types
These declarations are inconsistent and cause undefined behavior or link/compile problems depending on the situation:
// file_a.cpp
extern int total;
// file_b.cpp
long total = 0;
Put the declaration in one header and include it everywhere.
Assuming extern creates storage
It does not. This line is only a declaration:
extern float temperature;
A definition is still required:
float temperature = 21.5F;
Using globals when a parameter or object is better
Globals can make it unclear where a value changes. Prefer passing dependencies explicitly or keeping state inside a class unless shared global state is genuinely appropriate.
Forgetting the const rule
A namespace-scope const variable has internal linkage by default in C++. If you want to share it across source files using extern, declare and define it consistently:
// constants.h
extern const int maxUsers;
// constants.cpp
const int maxUsers = 100;
For many compile-time constants in modern C++, inline constexpr in a header is often a simpler option.
Comparisons
| Form | What it does | Creates storage? | Typical use |
|---|---|---|---|
extern int x; | Declares a variable defined elsewhere | No | Header declaration for a shared global |
int x; | Defines a global variable | Yes | The one .cpp file that owns the global |
int x = 10; | Defines and initializes a global variable | Yes | The one .cpp file that owns it |
static int x; at namespace scope | Defines a variable usable only in that source file | Yes | Private file-level state |
inline int x = 10; (C++17+) | Allows one header definition shared program-wide | Yes, with special inline rules | Header-defined global configuration/state when appropriate |
constexpr int x = 10; | Defines a compile-time constant | Yes conceptually, often optimized away | Constants local to a header or source file |
extern vs static at namespace scope
externsupports sharing one variable between source files.staticgives a name internal linkage, so other source files cannot refer to it by name.
// metrics.cpp
static int internalCounter = 0; // Only metrics.cpp can access it.
extern vs inline variables
Since C++17, an inline variable may be defined in a header without causing duplicate definitions:
// settings.h
inline int maxRetries = 3;
This is convenient for small header-level values, but it still creates global state. Use it deliberately.
Cheat Sheet
// Header: declaration only
extern int itemCount;
// Exactly one .cpp file: definition
int itemCount = 0;
externmeans “defined elsewhere.”- An
externvariable declaration does not allocate storage. - Put shared declarations in headers.
- Provide exactly one non-
inlinedefinition across the entire program. - Include the header in the
.cppfile that contains the definition too. - Functions are external by default;
externis usually omitted from normal function declarations. - Namespace-scope
staticmakes a variable private to one source file. - C++17
inlinevariables can be defined in headers:
inline int retryLimit = 3;
extern "C"concerns C linkage and is a separate use ofextern.
FAQ
What does extern mean in C++?
For a variable declaration, it means the variable is defined elsewhere in the program. The current file can use it without creating another variable.
Does extern int x; allocate memory for x?
No. It only declares the name and type. A definition such as int x = 0; must exist in one source file.
Should I put extern declarations in header files?
Usually, yes. Put the declaration in a header and include it wherever needed. Put the single definition in one .cpp file.
Can I define an extern variable in the same statement?
Yes. An initializer makes it a definition:
extern int x = 10;
This is valid, but it is uncommon. Prefer int x = 10; for clarity when defining the variable.
Why do I get a multiple-definition linker error?
You likely defined the same global variable in more than one source file, often by placing int x = 0; in a header. Change the header to extern int x; and keep one definition in a .cpp file.
Why do I get an undefined reference or unresolved external error?
The program declared a variable with extern but the linker could not find a matching definition. Add the definition and ensure its .cpp file is part of the build.
Is extern recommended for global variables?
It is the correct mechanism when you intentionally need a traditional shared global. However, prefer encapsulation, function parameters, or objects when they make dependencies clearer.
Is extern "C" the same as extern int x?
No. extern int x declares a variable with external linkage. extern "C" specifies C language linkage, mainly for compatibility with C code and libraries.
Mini Project
Description
Create a tiny application settings module shared by multiple C++ source files. This demonstrates how one file owns a global setting while other files access the same setting through an extern declaration in a header.
Goal
Build a program that changes a shared retry limit in main.cpp and reads the updated value from another source file.
Requirements
Create a header that declares maxRetries with extern.
Define and initialize maxRetries in exactly one .cpp file.
Create a function in a separate source file that prints the current retry limit.
Change maxRetries in main.cpp and show that the function observes the new value.
Compile all source files together.
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.