LEARNING · STAGE 00 · PROGRAMMING BASICS

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.

main.cppC++
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:

Three levels of nestingC++
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 declaredWhere visibleWhere NOT visible
At the beginning of main()Everywhere until the end of main(), including all nested blocksNowhere outside main()
Inside a first-level { } blockInside that block and in all blocks nested within itIn main() after that block closes; in other, neighboring blocks
Inside a function parameterInside that function's body (lesson seven)Anywhere else in the program, including other functions
Inside the body of an if/loopInside that if/loop and blocks nested within itAfter 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 visibleOutside 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.

main.cppC++
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:

Reading several values consecutivelyC++
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.

Step 1 — the parameter is used inside the functionC++
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.

Step 2 — a local variable is added inside the functionC++
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.

Step 3 — a call from main() shows the boundaryC++
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.

Worse — the same name in two scopesC++
int score = 10;
{
    int score = 99;
    std::cout << score << "\n";
}
std::cout << score << "\n";
console (illustration)
./main 99 10 Both numbers are correct, but the same name means different things on different lines.

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.

Better — different names for different valuesC++
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.

Best of all — the output also explains itselfC++
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 bonus after 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 score to double and try entering a fractional number — cin will read it whole, including the fractional part.

09Live Debugging: Common Mistakes

Accessing a variable outside its scope
{
    int bonus = 5;
}
std::cout << bonus;   // bonus existed only inside the block above
console (illustration)
g++ main.cpp -o main main.cpp: In function 'int main()': main.cpp:4:15: error: 'bonus' was not declared in this scope 4 | std::cout << bonus; | ^~~~~ Compilation failed.

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.

Forgot #include <iostream> for cin
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.

Variable shadowing is confusing

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.

★★ 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.

★★★ 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.

11Check Yourself

Find the error
The compiler will refuse to build this code. Why?
{
  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).

Predict the result
What will this program output, and in what order?
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").

Why this way and not another?
Why is there a separate 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

TermMeaning
Scopethe part of the program where a variable can be referred to by name
Shadowinga variable in a nested block temporarily hides a variable with the same name outside
std::cinthe input stream — reads data entered by the user
The >> operatorreads 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 to std::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)?

🎮 How does this relate to Unreal Engine?

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:

Example (Unreal C++, for illustration)C++
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.