Reusing a name such as total is legal in several parts of a C++ program. That means reading from top to bottom is not enough to identify the object an expression uses. One small three-file program separates declaration, initialisation, scope and namespace-qualified lookup, because every use answers the same question: at this exact line, which declaration does this spelling name?
Give every C++ name four coordinates
A declaration introduces a name; a definition supplies the entity or storage. Here, extern int total; is a declaration and int total = 10; is the single definition. Initialisation gives the first value. Assignment changes an existing value.
For each use, check four coordinates: the spelling (total), the enclosing scope (block, function or namespace), the qualification (total, ::total or meter::total), and the storage duration.
Visibility asks whether lookup can select the name here. Lifetime asks whether the object exists now.
Declaration | Initial value | Identity |
|---|---|---|
Outer local | 5 | Local object in |
Block local | 7 | Different local object |
Global | 99 | Global-namespace object |
| 10 | Object in namespace |
These equal spellings name four distinct objects.
Build one declaration, one definition and three visible totals
The header exposes names. counter.cpp owns the namespace variable's definition and the function definition. main.cpp defines the unrelated global object and creates two local objects.
counter.hpp
#ifndef COUNTER_HPP
#define COUNTER_HPP
namespace meter {
extern int total;
int add(int amount);
}
#endifcounter.cpp
#include "counter.hpp"
namespace meter {
int total = 10;
int add(int amount) {
int before = total;
total += amount;
return before;
}
}main.cpp
#include <iostream>
#include "counter.hpp"
int total = 99;
int main() {
int total = 5;
std::cout << total << ' ' << ::total << ' '
<< meter::total << '\n';
{
int total = 7;
int previous = meter::add(3);
std::cout << total << ' ' << previous << ' '
<< meter::total << '\n';
}
std::cout << total << ' ' << ::total << ' '
<< meter::total << '\n';
}Build and run it:
g++ -std=c++17 main.cpp counter.cpp -o scope_demo
./scope_demoThe exact output is:
5 99 10
7 10 13
5 99 13Trace name lookup line by line
At the first output, unqualified total finds the nearest declaration in main and prints 5. ::total starts in the global namespace and prints 99; meter::total selects the namespace object and prints 10.
The inner total hides the outer local and prints 7. In meter::add(3), amount is 3, before copies 10, and total += amount computes 10 + 3 = 13. Returning 10 initialises previous, producing 7 10 13.
After the block, its total and previous are gone. The outer local is visible again at 5, the global remains 99, and the namespace object retains 13. Shadowing did not overwrite the hidden object; lookup temporarily stopped selecting it.

Separate scope, storage duration and linkage
Scope says where a name can be used. The inner total has block scope, amount and before belong to the function body, and meter::total belongs to namespace meter.
Storage duration says how long an object exists. Locals exist during their block or function execution; both namespace-scope objects exist for the program's run. Scope is not lifetime.
Linkage connects declarations across translation units. The header declares extern int total;, and exactly one source defines int total = 10;. A plain non-inline definition in the shared header would produce multiple definitions, not one shared object.

Use namespaces as collision boundaries
Two libraries may reasonably choose the same spelling:
namespace ui { int timeout_ms = 250; }
namespace net { int timeout_ms = 1000; }ui::timeout_ms + net::timeout_ms is unambiguous: 250 + 1000 = 1250. A block-level using ui::timeout_ms; makes unqualified timeout_ms select 250. But using namespace ui; using namespace net; makes it ambiguous.
Qualify names at boundaries and avoid broad using-directives in headers. Use the same tracing discipline when comparing classic programs in C, Java and Python, but remember that these namespace rules are C++ rules.
Repair the traps that make scope feel random
Uninitialised local:
int attempts;does not promise zero. Useint attempts = 0;orint attempts{};either form starts it at0.Self-shadowing initializer: after outer
int count = 4;, innerint count = count + 1;binds the initializer'scountto the new inner object, which has no value of its own yet, so the result is undefined. Useint next_count = count + 1;, which produces5.Duplicate definition:
int total = 10;in a shared header creates a definition in every including translation unit. Keep the exactexterndeclaration and one.cppdefinition.Namespace pollution: importing both namespaces makes unqualified
timeout_msambiguous. Writeui::timeout_msornet::timeout_msat the use site.
Names select objects first; pointers then store addresses. Pointers in C for GATE builds that memory layer. C does not have C++ namespaces.
Solve lookup questions with arrows
Consider global int score = 40;, namespace quiz { int score = 70; }, local int score = 10;, and inner-block int score = 20;. Inside the inner block:
std::cout << score << ' ' << ::score << ' ' << quiz::score;Draw three arrows: unqualified score reaches the block declaration, ::score the global declaration, and quiz::score the namespace member. The output is 20 40 70.
For a compile-error drill, define namespace A { int x = 1; } and namespace B { int x = 2; }, import both, then use unqualified x. Two candidates make it ambiguous at compile time. Repair it with A::x or B::x.
Common questions ask you to predict output, identify declarations and definitions, detect hiding, or choose a qualified name. Lookup arrows handle all four without memorising "local wins."
The short version and the next experiment
Ask five questions: Where is the name declared? What is the nearest scope? Is the use qualified? Which object is initialised or modified? How long does it live? The trace is block 7, outer local 5, global 99, namespace 10 -> 13.
Compile the program, rename only the inner total, and predict all three rows before rerunning. Then remove one qualifier at a time and explain the lookup before compiling.
For a structured route through the language fundamentals, continue with the C++ Programming course. If your goal also includes other languages and placement-style practice, use Coding for Placements. Compare the broader programming paths in the Coding and Skills category.




