Skip to content

Control Statements

By default, a program runs one statement after another from top to bottom. Control statements let you change that flow: take one path or another based on a condition, repeat a block of code, or stop a loop early.

Three families:

Family Examples Purpose
Conditional if, else, else if, switch Choose between paths
Loop while, do-while, for, range-based for Repeat code
Jump break, continue, return Exit a block or function early

if and else

if (temperature > 80) {
    std::cout << "Cooling down\n";
}

The condition in parentheses must produce a bool (or something convertible to one). If it is true, the block runs; otherwise it is skipped.

To handle the other case:

if (temperature > 80) {
    std::cout << "Cooling down\n";
} else {
    std::cout << "Normal\n";
}

For more than two outcomes, chain with else if:

if (temperature > 80) {
    std::cout << "Too hot\n";
} else if (temperature < 10) {
    std::cout << "Too cold\n";
} else {
    std::cout << "Fine\n";
}

Only the first matching branch runs. Once a branch is taken, the rest are skipped.

Use braces even for single-statement bodies. It is one extra line and avoids a surprising class of bugs when someone adds a second statement later.


switch

When you are comparing one value against several constants, switch is clearer than a long else if chain:

switch (gear) {
    case 1: std::cout << "First\n";  break;
    case 2: std::cout << "Second\n"; break;
    case 3: std::cout << "Third\n";  break;
    default: std::cout << "Unknown\n";
}

Two things to know:

  1. Always include break at the end of each case unless you specifically want execution to fall through to the next case. Forgetting break is a classic bug: execution silently continues into the next case.
  2. switch only works with integer-like values (int, char, enumerations). It cannot switch on a std::string or a double.

A subtle trap: all the cases share one scope — the single block after switch (...). So a variable declared in one case is still in scope in the cases below it, and C++ forbids jumping over its initialisation. This innocent-looking code does not compile:

switch (gear) {
    case 1:
        int chosen = gear * 10;   // declared here
        std::cout << chosen << "\n";
        break;
    case 2:                       // jumping here would skip
        std::cout << "Second\n";  // the line that sets up 'chosen'
        break;
}

The compiler rejects it with something like "jump to case label crosses initialization of 'int chosen'": reaching case 2 would bypass the line that sets up chosen, yet chosen is still in scope there, so the language refuses.

Give the case its own scope with braces {}, and the variable lives and dies inside them:

switch (gear) {
    case 1: {
        int chosen = gear * 10;
        std::cout << chosen << "\n";
        break;
    }
    case 2:
        std::cout << "Second\n";
        break;
}

Now chosen exists only between the braces, so nothing leaks into case 2. Rule of thumb: the moment a case declares a variable, wrap that case in {}.


while

Repeat a block as long as a condition is true:

int countdown = 5;
while (countdown > 0) {
    std::cout << countdown << "...\n";
    --countdown;
}
std::cout << "Go!\n";

The condition is checked before each iteration. If it is false at the start, the body runs zero times.

The number-one bug with while loops is forgetting to make progress toward the exit condition:

int i = 0;
while (i < 10) {
    std::cout << i << "\n";
    // forgot ++i, infinite loop
}

If your program hangs, this is the first place to look.


do-while

Like while, but the condition is checked after the first iteration. The body therefore always runs at least once:

int input = 0;
do {
    std::cout << "Enter a positive number: ";
    std::cin >> input;
} while (input <= 0);

Use this when the work must happen before you know whether to continue. Common pattern: "read input until the user provides something valid."


for

When you know how many times to loop, for is the cleanest form:

for (int i = 0; i < 5; ++i) {
    std::cout << i << "\n";
}
// prints 0, 1, 2, 3, 4

The three parts inside the parentheses are:

  1. Initialisation (int i = 0): runs once, before the loop starts.
  2. Condition (i < 5): checked before each iteration. Loop ends when false.
  3. Update (++i): runs after each iteration.

A for loop is just a while loop with the parts arranged for visibility. Use it whenever you have a counter.


Range-based for

For visiting every element of a container, the range-based for is shorter and harder to get wrong than a counter-based for:

std::vector<int> readings{42, 17, 99, 8};

for (int value : readings) {
    std::cout << value << "\n";
}

If you do not need to modify the elements, prefer const auto& to avoid copying:

for (const auto& value : readings) {
    std::cout << value << "\n";
}

To modify the elements in place, take a non-const reference:

for (auto& value : readings) {
    value *= 2;
}

The & here makes value be the element itself rather than a copy of it: without it, each value is a fresh copy and changes to it are thrown away at the end of the iteration; with it, writing to value writes straight into the vector. (const on top of that promises you will only read, never write.) References are covered properly in Values, References, and Pointers; here you only need to know the & avoids the copy and lets you edit in place.


break, continue, return

These three change the flow inside a loop or function.

for (int i = 0; i < 100; ++i) {
    if (i == 10) {
        break;     // exit the loop entirely
    }
    if (i % 2 == 0) {
        continue;  // skip the rest of this iteration, go to the next
    }
    std::cout << i << "\n";
}
  • break exits the innermost loop or switch.
  • continue skips the rest of the current iteration and moves to the next.
  • return exits the function entirely (and optionally returns a value).

Choosing the right tool

Situation Use
Two or three branches based on a condition if / else if / else
Many branches on one integer-like value switch
Repeat until a condition becomes false while
Loop body must run at least once do-while
Fixed number of iterations with a counter for
Visit every element of a container range-based for
Exit a loop early break
Skip to the next iteration continue