Question

When to Use extern in C++: Declarations and Definitions

cppvariable-declaration

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.

Take Quiz

Syntax and Examples

The usual pattern is:

  1. Put the extern declaration in a header file.
  2. Include that header wherever the variable is needed.
  3. Put one definition in exactly one .cpp file.

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:

  1. The compiler processes main.cpp and includes counter.h.
  2. It sees extern int requestCount;, so it knows requestCount is an int but does not allocate storage there.
  3. ++requestCount is valid because its type is known.
  4. The compiler processes counter.cpp separately.
  5. It sees int requestCount = 0;, which creates the actual variable and initializes it to 0.
  6. The linker combines both compiled files. It resolves the use in main.cpp to the definition in counter.cpp.
  7. At runtime, requestCount starts at 0, is incremented to 1, 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.

Take Quiz

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

FormWhat it doesCreates storage?Typical use
extern int x;Declares a variable defined elsewhereNoHeader declaration for a shared global
int x;Defines a global variableYesThe one .cpp file that owns the global
int x = 10;Defines and initializes a global variableYesThe one .cpp file that owns it
static int x; at namespace scopeDefines a variable usable only in that source fileYesPrivate file-level state
inline int x = 10; (C++17+)Allows one header definition shared program-wideYes, with special inline rulesHeader-defined global configuration/state when appropriate
constexpr int x = 10;Defines a compile-time constantYes conceptually, often optimized awayConstants local to a header or source file

extern vs static at namespace scope

  • extern supports sharing one variable between source files.
  • static gives 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.

Take Quiz

Cheat Sheet

// Header: declaration only
extern int itemCount;

// Exactly one .cpp file: definition
int itemCount = 0;
  • extern means “defined elsewhere.”
  • An extern variable declaration does not allocate storage.
  • Put shared declarations in headers.
  • Provide exactly one non-inline definition across the entire program.
  • Include the header in the .cpp file that contains the definition too.
  • Functions are external by default; extern is usually omitted from normal function declarations.
  • Namespace-scope static makes a variable private to one source file.
  • C++17 inline variables can be defined in headers:
inline int retryLimit = 3;
  • extern "C" concerns C linkage and is a separate use of extern.

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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more