Scope and lifetime
Picture each block of a program as a room with its own boxes. A box placed in a room is visible only there: outside the door, you cannot find it by its sticker. In C++, curly braces serve as the room's walls. The name of a variable declared inside them works only up to the closing brace. Outside them, the name is unknown:
#include <iostream>
int main() {
int total = 0;
{
int bonus = 10;
total = total + bonus;
}
std::cout << total + bonus << '\n'; // ✗
}error: use of undeclared identifier 'bonus'The part of a program where a variable's name can be used is its scope. It starts at the declaration and ends at the closing brace of the block, the code between a pair of curly braces. A function body is a block too. So variables with the same name in different functions are different variables.
Hiding a name
An inner block may declare a variable with the same name as an outer one. This is a new variable, like a box with the same sticker placed in an inner room. Until the end of the block, the name denotes it, and the outer one is hidden. Such shadowing ends at the closing brace, and the outer variable is visible again with its old value:
#include <iostream>
int main() {
int score = 5;
{
int score = 100;
std::cout << score << '\n'; // 100
}
std::cout << score << '\n'; // 5
}Lifetime
A variable's lifetime is the time during which it exists in memory. Scope belongs to a name in the program text, while lifetime belongs to the variable itself at run time. An ordinary variable inside a function is created when execution reaches its declaration and destroyed at the end of its block. On the next call to the function, it is created again. It is like a box that is thrown out when you leave the room, with a new one brought on the next visit.
A variable marked static lives until the program ends: its box stays on a shelf in the room, and on every visit the contents are still there. It is initialized once, not on every call, and keeps its value between calls:
#include <iostream>
void visit() {
static int visits = 0;
int steps = 0;
visits = visits + 1;
steps = steps + 1;
std::cout << visits << ' ' << steps << '\n';
}
int main() {
visit(); // 1 1
visit(); // 2 1
visit(); // 3 1
}steps starts from 0 on every call, while visits keeps counting. static changes only the lifetime: visits is still visible only inside visit. Interviewers often ask what a function with a static counter prints.
Step through this program and watch the frames: visits sits in the static frame and survives every call, while steps appears and disappears with visit.
1–2#include <iostream>3void visit() {4 static int visits = 0;5 int steps = 0;6 visits = visits + 1;7 steps = steps + 1;8 std::cout << visits << " ";9 std::cout << steps << "\n";10}11 12int main() {13 visit();14 visit();15 visit();16}
visits already exists and holds 0, though visit has not been called yet
Common mistake
You meant to change the outer variable inside a block but wrote the type again out of habit:
#include <iostream>
int main() {
int total = 0;
{
int bonus = 10;
int total = bonus; // ✗
std::cout << total << '\n'; // 10
}
std::cout << total << '\n'; // 0
}The line with int declares a new variable that hides the outer total, and the outer one stays 0. The compiler reports neither an error nor a warning. To change the outer variable, you need an assignment without a type: total = total + bonus;.
Key points
- Scope is the part of a program where a name can be used: from the declaration to the closing brace of the block.
- A variable with the same name in an inner block hides the outer one until the block ends, and the outer one keeps its old value.
- An ordinary variable in a function is created anew on every call, while a
staticone is initialized once and keeps its value between calls. staticchanges the lifetime, not the scope.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.