Scope, duration, and linkage

Three simple questions for understanding where a name works, how long an object lives, and whether declarations share one entity.

The three questions

These concepts describe different things. Do not try to determine all three at once:

Concept Ask this question Describes
Scope Where can I use this name in the source code? Visibility of a name
Duration When is this object created and destroyed? Lifetime of an object
Linkage Do declarations of this name refer to the same entity? Identity across scopes or files

Scope: where can I use the name?

Block scope

A name declared inside a block ({ ... }) can be used from its declaration to the end of that block, including nested blocks.

void printTotal(int amount) // amount has block scope
{
	int total { amount };    // total has block scope

	if (total > 0)
	{
		std::cout << total;   // okay: nested block
	}
}

// amount and total cannot be used here

Local variables, function parameters, and types declared inside a block have block scope.

Global scope

A name declared outside a function can be used from its declaration to the end of the file. Global variables, functions, namespaces, and types declared outside a block have global scope.

Duration: how long does the object live?

Duration Created Destroyed Common examples
Automatic When execution reaches its definition When its block ends Ordinary local variables, parameters
Static Before first use Around program shutdown Global variables, static local variables
Dynamic When explicitly created When explicitly destroyed Objects created with new and destroyed with delete
void visit()
{
	int visitsThisCall {};        // new object on every call
	static int totalVisits {};    // one object that survives between calls
}

Both names have block scope, but their objects have different durations.

Linkage: which entity does the name mean?

Linkage Meaning Common examples
None Every declaration names a separate entity Local variables, parameters
Internal Declarations within one translation unit can name the same entity static globals/functions, global const, unnamed namespaces
External Declarations across the program can name the same entity Ordinary functions, non-const globals, extern globals
static int fileCount {}; // internal: private to this translation unit
int sharedCount {};      // external: other files can declare and use it

See Internal and external linkage for the full static, extern, declaration, and definition rules.

Put them together

int g_score {}; // global scope, static duration, external linkage

void play(int rounds) // play: global scope and external linkage
{
	int score {};       // block scope, automatic duration, no linkage
	static int games {}; // block scope, static duration, no linkage
}

namespace
{
	int hiddenScore {}; // global scope, static duration, internal linkage
}

The important combinations are:

Declaration Scope Duration Linkage
int value {}; inside a function Block Automatic None
static int value {}; inside a function Block Static None
int g_value {}; outside a function Global Static External
static int g_value {}; outside a function Global Static Internal
const int g_value { 1 }; outside a function Global Static Internal
extern const int g_value { 1 }; outside a function Global Static External

Forward declarations

A forward declaration introduces a name without defining the entity:

void print(int value);  // function declaration
extern int g_count;     // variable declaration
extern const int limit; // const variable declaration
  • Function declarations do not need extern.
  • Variable declarations use extern and have no initializer.
  • constexpr variables cannot be forward-declared as constexpr because the compiler must see their value.