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
externand have no initializer. constexprvariables cannot be forward-declared asconstexprbecause the compiler must see their value.