Scope and Storage Classes in C++
In this lesson: Scope and storage classes in C++ — local, global, static, extern, thread_local — with examples. In Hindi & English.
Scope and storage — two different questions
Every variable answers two questions:
- Scope: *where* in the code can I use this name?
- Storage duration (lifetime): *when* is the variable created and destroyed?
Beginners often mix these up. A static variable inside a function, for example, can only be *used* inside that function (small scope) but *exists* for the whole run of the program (long lifetime). Storage class specifiers — static, extern, thread_local, mutable — control lifetime and linkage.
Types of scope
| Scope | Declared | Visible |
|---|---|---|
| Block (local) | Inside { } — function body, loop, if | From declaration to the closing } |
| Function parameter | In the parameter list | Whole function body |
| Global (file) | Outside all functions and classes | From declaration to end of file (and other files with extern) |
| Namespace | Inside namespace X { } | Inside the namespace, or as X::name |
| Class | Inside a class | Members of the class; outside via object or Class:: |
#include <iostream>
int total = 100; // global
int main() {
int total = 5; // local, hides the global
{
int bonus = 2; // block scope
std::cout << total + bonus << "\n"; // 7
}
// std::cout << bonus; // error: bonus not in scope here
std::cout << ::total << "\n"; // 100 — global via ::
return 0;
}When a local name hides a global one, the scope resolution operator ::name reaches the global. Hiding names like this is legal but confusing — better to choose different names.
Automatic storage (the default)
Local variables without a specifier have automatic storage: they are created when execution reaches the declaration and destroyed when the block ends. Each function call gets fresh copies. Uninitialised automatic variables contain garbage, so always initialise them.
static local variables
A static local variable is initialised once and keeps its value between calls.
#include <iostream>
void markAttendance() {
static int count = 0; // created once, lives until program ends
int calls = 0; // automatic: recreated every call
count++;
calls++;
std::cout << "count = " << count << ", calls = " << calls << "\n";
}
int main() {
markAttendance();
markAttendance();
markAttendance();
return 0;
}count remembers its value; calls starts at 0 every time.
static globals and static class members
- A
staticglobal variable or function is visible only in its own.cppfile (internal linkage). In modern C++ an anonymous namespace does the same job. - A
staticclass member belongs to the class, not to each object — one shared copy. See static members.
extern: sharing a global across files
// config.h
extern int maxStudents; // declaration only — no memory yet
// config.cpp
int maxStudents = 40; // the one definition
// main.cpp
#include "config.h"
#include <iostream>
int main() {
std::cout << maxStudents << "\n"; // 40
}Every file that includes config.h can use maxStudents, but only config.cpp defines it. Defining it in the header instead would cause "multiple definition" errors.
thread_local, mutable, register and auto
- thread_local (C++11): each thread gets its own copy of the variable, living as long as the thread.
- mutable: lets a data member be changed even inside a
constmember function — used for caches and counters. - register: once a hint to keep a variable in a CPU register; deprecated in C++11 and removed in C++17. Compilers optimise this themselves.
- auto: in old C/C++ meant "automatic storage". Since C++11 it means "deduce the type":
auto x = 3.5;makesxadouble.
class Report {
mutable int views = 0;
public:
void show() const { views++; /* allowed because mutable */ }
};Storage classes compared
| Specifier | Lifetime | Default value | Typical use |
|---|---|---|---|
| (none, local) | Until block ends | Garbage | Ordinary local variables |
static (local) | Whole program | 0 | Counters, one-time setup |
global / static global | Whole program | 0 | Program-wide settings |
extern | Whole program (defined elsewhere) | — (declaration) | Share a global across files |
thread_local | Life of the thread | 0 | Per-thread data |
mutable | Same as the object | — | Change a member in a const function |
Common mistakes
- Returning a pointer or reference to a local (automatic) variable — it is destroyed when the function ends.
- Expecting a non-static local to remember its value between calls.
- Defining (not just declaring) a global in a header included by many files.
- Overusing global variables, which makes programs hard to test and debug.
- Writing
register int i;in modern C++ — it is an error in C++17.
Write a function nextRollNumber() that returns 1, 2, 3, … on successive calls using a static local variable. Then split it into two files and share a global schoolCode using extern.
Summary
- Scope = where a name is visible; lifetime = how long the variable exists.
- Local variables are automatic: created and destroyed with their block.
staticlocals keep their value between calls;staticglobals are file-private.externdeclares a global defined in another file.registeris gone;autonow means type deduction;mutableandthread_localare special cases.
Related: functions, static members, namespaces.
Frequently Asked Questions
What are the storage classes in C++?
static, extern, thread_local and mutable. Variables without a specifier inside a function have automatic storage. The old register specifier was removed in C++17, and auto now means type deduction rather than a storage class.What is the difference between scope and lifetime?
static local variable has block scope (usable only inside its function) but lives for the whole program.What does a static local variable do in C++?
When should I use extern in C++?
extern to declare a global variable that is defined in another source file, so several .cpp files can share one variable. Write extern int count; in a header and int count = 0; in exactly one .cpp file.