Scope, Input, and Final Practice
The last lesson of Stage 00 closes the two remaining questions — where exactly a variable "lives" and how a program gets data not from code, but from the user (std::cin) — and then brings together everything from the stage in one final practice.
01What You Will Learn
- Scope — where a variable exists, and where it is no longer "known"
- How function parameters and variables inside blocks relate to scope
std::cin— reading data entered by the user- A final practice that uses everything from Stage 00 at once
02Core Idea: Rooms in a House
Scope is like rooms in a house. An item left in one room (a variable declared in a { } block) is accessible as long as you are in that room or in any room inside it (a nested block) — but it cannot be seen or reached from a neighboring room or from the hallway outside. When you leave the room (the block ends with a closing }), everything that was left only there becomes inaccessible — not because it "disappeared", but because you are no longer where it is. That is why you can put items with the same name in two different, non-nested rooms without creating confusion — they are simply in different places.
03Scope — Where a Variable Exists
Let's translate the room analogy to real code and see exactly where the boundary lies. The rule is simple, though not immediately obvious until you see it in action: a variable exists from the point of declaration to the end of the { } block in which it is declared — and becomes inaccessible immediately after the closing brace. This is called scope: the part of the program where a variable can be referred to by name.
int main()
{
int score = 10; // score exists until the end of main()
{
int bonus = 5; // bonus exists only inside this block
std::cout << score + bonus; // both score and bonus are visible here
}
// bonus is no longer accessible here — the block above has ended
std::cout << score; // score is still visible — main() has not ended
return 0;
}
This same rule was already at work in previous lessons, just without a name: a function parameter (lesson seven) is visible only inside that function's body — that is its scope; variables declared inside the body of an if or a loop are visible only inside that block. Two different blocks can use the same variable name independently of each other — this is not a conflict, because their scopes do not overlap (see the practice below).
Nesting can go deeper than one level — the rule is the same at any depth:
int main()
{
int a = 1; // visible everywhere in main(), including both blocks below
{
int b = 2; // visible in this block and in the one nested below, but not in main() after
{
int c = 3; // visible only here, inside the deepest block
std::cout << a + b + c; // all three are visible here: a, b, and c
}
// c is no longer accessible — the deepest block has ended
// std::cout << c; // compilation error if uncommented
}
// b is also no longer accessible
return 0;
}
The general rule: a variable is visible in its own block and in all blocks nested inside it, but it is not visible outside, nor in blocks declared earlier or later at the same level. Below is the same idea in table form:
| Where declared | Where visible | Where NOT visible |
|---|---|---|
At the beginning of main() | Everywhere until the end of main(), including all nested blocks | Nowhere outside main() |
Inside a first-level { } block | Inside that block and in all blocks nested within it | In main() after that block closes; in other, neighboring blocks |
| Inside a function parameter | Inside that function's body (lesson seven) | Anywhere else in the program, including other functions |
Inside the body of an if/loop | Inside that if/loop and blocks nested within it | After the closing } of that if/loop |
| With the same name as an outer variable, in a nested block (shadowing) | Inside the nested block — only the inner variant is temporarily visible | Outside the nested block — the compiler sees the outer one again, as if the inner one never existed (more in the "Worse → Better" section below) |
04std::cin — Reading Input
Everything the program has output so far was either a literal or the result of a computation in the code itself. std::cin (input stream) is the symmetric counterpart to std::cout: it reads data entered by the user instead of outputting it. The same #include <iostream> brings in both cout and cin — they are from the same library.
int score = 0;
std::cout << "Enter score: ";
std::cin >> score; // waits for user input, stores it in score
std::cout << "You entered: " << score;
The >> operator on cin is a mirror image of << on cout: data "flows" into the variable, not out of it. The type of the variable on the right determines how cin parses the entered text — for int score it expects a number and will stop at the first space or newline.
Like << on cout, the >> operator can be repeated consecutively in a single statement — then cin reads several values one after another, in order:
int width = 0;
int height = 0;
std::cout << "Enter width and height (separated by a space): ";
std::cin >> width >> height; // two values in a single statement
int area = width * height;
std::cout << "Area: " << area;
The user can enter both numbers separated by a space on one line (for example, "4 5") or on different lines — cin ignores spaces and newlines between values and simply fills width, then height, in the order they are listed after cin.
05What Makes a Program a "Real Console Program"
All programs in this stage were one-directional: code on the input side, text on the screen on the output side, without user participation during execution. A full-fledged console program is usually built as an "input → processing → output" loop: get data from the user (cin), do something with it (variables, operators, conditions, loops, functions — everything already covered), and show the result (cout). The final practice below is built exactly according to this scheme.
06Function Parameter Scope Step by Step
A function parameter (lesson seven) is also a variable with its own scope, except its "room" is the function body. Below is the same function, with one more detail added at each step, to make it clear where exactly this room begins and ends.
void printDoubled(int value)
{
std::cout << value * 2 << "\n";
}
value exists and is accessible by name only inside the body of printDoubled — from the opening to the closing { } of this function.
void printDoubled(int value)
{
int doubled = value * 2; // a local variable, its own "room" — also the function body
std::cout << doubled << "\n";
}
doubled is an ordinary local variable with the same scope rule as any { } block: it is visible until the end of the function, but not outside. Both value and doubled live in the same "room" — the function body.
void printDoubled(int value)
{
int doubled = value * 2;
std::cout << doubled << "\n";
}
int main()
{
printDoubled(21);
// value and doubled do not exist here — they lived only inside printDoubled
// std::cout << value; // compilation error if uncommented
// std::cout << doubled; // also a compilation error
return 0;
}
From main(), neither value nor doubled is visible — both variables existed only for the duration of printDoubled and disappeared as soon as the function finished. This is the same "room" boundary as with an ordinary { } block, except for functions it is called by a separate term — function scope.
07Worse → Better: Variable Shadow
The task is to output bonus points inside a nested block, and then the main score outside. Both values exist simultaneously, but in different scopes.
int score = 10;
{
int score = 99;
std::cout << score << "\n";
}
std::cout << score << "\n";
The program works correctly — the nested score temporarily shadows the outer one, and each reference sees what it should. But when reading the code (especially if the blocks get longer), it is easy to mistake this for a bug: "why did score suddenly become 99, and then 10 again?" — without a careful look at the curly braces, the answer is not obvious.
int score = 10;
{
int bonusPoints = 99;
std::cout << bonusPoints << "\n";
}
std::cout << score << "\n";
The same values and the same output order — but now, from the names alone, it is clear that these are two different values, not one and the same value that "magically" changed between lines.
int score = 10;
{
int bonusPoints = 99;
std::cout << "Bonus: " << bonusPoints << "\n";
}
std::cout << "Score: " << score << "\n";
Labeled output (a technique from the very first lesson) together with meaningful names (a technique from the third lesson) — the code and its result are now understandable without needing to look at the variable declarations earlier in the file.
08Try Changing It
- In the example from the "Scope" section, try accessing
bonusafter the closing brace of its block — the compiler will refuse: a name outside its scope does not exist. - In the example from the "std::cin" section, change the type of
scoretodoubleand try entering a fractional number —cinwill read it whole, including the fractional part.
09Live Debugging: Common Mistakes
{
int bonus = 5;
}
std::cout << bonus; // bonus existed only inside the block above
The compiler gives an error "variable was not declared" — from its point of view, such a name simply does not exist at this point in the program, not that the "value was lost" or anything like that.
std::cin >> score; // #include <iostream> is missing above
The same reason as for cout in the very first lesson (and the same characteristic compiler error "is not a member of 'std'", already encountered in the third lesson) — cin is also defined in iostream; without the include, the compiler will not find it.
A full breakdown with an output example is in the "Worse → Better" section above. In this course, it is worth avoiding reusing the same name in nested blocks, unless it literally refers to the same thing (as in the practice below, where the blocks are not nested, but go sequentially).
10Final Practice for Stage 00
The second task below (★★★) is the finale of the character program we have been building from lesson to lesson, starting with "Variables": hp appeared there, types and stats — in "Basic Data Types", operators and conditions — in "Operators" and "Conditions", gradeFor — in the previous lesson about functions. Here it all comes together into one real console program with user input.
★ Write two separate { } blocks; in each, declare a score variable with its own value (different in each block) and output it. This will show that variables in different blocks are independent, even with the same name.
The two blocks should go one after another at the same level, not one inside the other — each with its own opening and closing { }, and each must output its score.
Solution: { int score = 10; std::cout << score << "\n"; } { int score = 20; std::cout << score << "\n"; } — two independent blocks in a row, not nested inside each other.
★★ Unlike the task above, now demonstrate shadowing specifically: declare int score = 10; at the beginning of main(), then open a nested { } block and inside it declare another int score with a different value (for example, 99), output it via std::cout, close the block, and then output score once more — outside, the value 10 should be visible again, as in the "Worse → Better" section above.
The second score must be declared INSIDE the nested { } block, not next to the first one, as in task 1 — otherwise it is not shadowing, but two independent blocks.
After the nested block closes with its }, you need another std::cout << score outside — that is the one that should show that the outer value (10) is visible again, not the one from inside the block.
Solution: int score = 10; { int score = 99; std::cout << score << "\n"; } std::cout << score << "\n"; — the second score is declared inside the nested block, and the final output is after its closing brace.
★★★ Stage 00 final practice — the final assembly of the character program. Write a function char gradeFor(int hp) (as in the previous lesson: 90 and above — 'A', 70–89 — 'B', below 70 — 'F') using a chain of if/else if/else. In main(), read the character name (std::string playerName) and their hp via std::cin, call gradeFor(hp), and output a full report: name, hp, and grade.
Steps: 1) signature char gradeFor(int hp) — takes an integer, returns a character; 2) body — three branches if/else if/else, each comparing hp to a number and returning its own letter; 3) in main(), first read both values via std::cin (after each prompt — the read immediately); 4) store the result of gradeFor(hp) in a variable; 5) one output that includes all three values — name, hp, and grade.
Start with the signature and body of gradeFor separately from main() — it takes int hp (not int&: it does not need to change anything in the original, only calculate and return), and inside — a chain of if / else if / else, where each branch compares hp to a threshold (for example, >= 90, >= 70) and returns its own letter. You need at least three different letters and at least one real comparison of hp with a number.
In main(), both std::cin statements must come immediately after their corresponding prompt and before the gradeFor call — first std::cin >> playerName;, then std::cin >> hp;. Without both values read into variables, there is nothing to pass to gradeFor.
Be sure to store the result of the call in a variable — char grade = gradeFor(hp); — rather than just calling the function by itself. If the grade is calculated but not stored anywhere and not output, the task is not accepted.
Solution: char gradeFor(int hp) { if (hp >= 90) { return 'A'; } else if (hp >= 70) { return 'B'; } else { return 'F'; } } — in main(): std::cin >> playerName; std::cin >> hp; char grade = gradeFor(hp); std::cout << playerName << " " << hp << " " << grade; — one final output including all three values.
11Check Yourself
{
int bonus = 5;
}
std::cout << bonus;Show explanation
bonus is declared inside the { } block and exists only until its closing brace (§03) — outside the block, the compiler simply does not know it and will produce an error like "'bonus' was not declared in this scope" (see §09).
int score = 1;
{
int score = 2;
std::cout << score;
}
std::cout << score;Show explanation
21. Inside the nested block, the inner score (value 2) temporarily shadows the outer one — the first output shows it. After the closing }, the inner score ceases to exist, and the outer score with value 1 is visible again (§07, "Worse → Better").
std::cout prompt ("Enter name: ", etc.) before each std::cin >> variable; in this lesson's practice? The program would work without it.Show explanation
Technically, yes — cin will read input even without a prompt. But a user running the program will simply see an empty blinking cursor and will not understand that input is expected, and in what order. The prompt is part of the "input → processing → output" loop (§05): it turns a program that silently waits for something into a program that explains itself to the user.
12Terms
| Term | Meaning |
|---|---|
| Scope | the part of the program where a variable can be referred to by name |
| Shadowing | a variable in a nested block temporarily hides a variable with the same name outside |
std::cin | the input stream — reads data entered by the user |
The >> operator | reads a value from cin into a variable (the mirror of << on cout) |
13What You Can Do Now
- Determine the scope boundaries of any variable — from declaration to the end of its
{ }block — and explain why accessing it from outside will not compile. - Recognize shadowing in someone else's code: understand which of the two same-named values is visible on a given line, and predict what will become visible again after the nested block closes.
- Read data entered by the user via
std::cin— both a single value and several consecutively — and build a program following the "input → processing → output" loop. - Assemble all eight Stage 00 topics into one working program: variables and types, operators and conditions, loops and functions, and now also user input instead of hardcoded numbers in the code.
14Summary
- A variable's scope is from its declaration to the end of the
{ }block in which it is declared. - Function parameters and variables inside if/loops/arbitrary blocks follow the same rule.
- The same name in different, non-nested blocks is not a conflict: the scopes do not overlap.
- Variable shadowing in a nested block compiles, but makes reading harder — in this course, it is worth avoiding.
std::cin >> variable;— reading user input, symmetric tostd::cout <<.- A full-fledged console program is an "input → processing → output" loop.
With this, Stage 00 is complete: eight lessons, from the first compiled line to a program that itself reads data from the player and calculates the result. Next begins Stage 01 — and its first lesson, const, answers a question that has not been raised even once so far: what prevents accidentally changing a value that, by its meaning, should not change at all (for example, a character's maximum HP)?
Scope works in Unreal C++ exactly the same way. The classic example is BeginPlay(), the method the engine calls once when an Actor appears in the game:
void AMyActor::BeginPlay()
{
Super::BeginPlay();
int32 StartingBonus = 100; // a local variable — lives only inside BeginPlay()
Health = MaxHealth + StartingBonus; // Health is a class member variable, visible in all methods
// StartingBonus still exists here...
}
// ...but here, in Tick() or any other method of the same class, StartingBonus is already inaccessible —
// its scope ended together with BeginPlay(), unlike Health
StartingBonus is a local variable with the same scope rule as in this lesson: visible only inside BeginPlay(). Health, on the other hand, is a class member variable (Stage 07+), and it has a different, wider scope — it is accessible from any method of this Actor, including Tick(). std::cin is almost never used directly in a game — instead of console input, Unreal has an Input system (keyboard/gamepad/mouse, Enhanced Input), but the very idea of "wait for data from outside, then process it" remains the same.