Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 3 of 6

Three loops, one job: repeat something. The difference is when the condition gets checked and where the bookkeeping lives.

while: check first, maybe never run

#include <iostream>

int main() {
    int countdown = 3;

    while (countdown > 0) {
        std::cout << countdown << "\n";
        --countdown;
    }
    std::cout << "Liftoff\n";
    return 0;
}
3
2
1
Liftoff

The condition is checked before each pass, so a while whose condition starts false runs zero times. Use it when you don't know in advance how many iterations you need.

The danger is forgetting to change the condition variable:

int countdown = 3;
while (countdown > 0) {
    std::cout << countdown << "\n";
    // forgot --countdown — runs forever
}

An infinite loop isn't a compiler error; the program just hangs. If yours does, Ctrl+C stops it, and the first thing to check is whether the condition variable actually changes.

do-while: run first, then check

#include <iostream>

int main() {
    int value = 10;

    do {
        std::cout << "ran once with value " << value << "\n";
    } while (value < 5);

    return 0;
}
ran once with value 10

The condition was false from the start, and the body still ran — because do-while checks after. It always executes at least once.

Note the semicolon after while (...). It's required, and forgetting it produces a confusing error.

do-while is much rarer than the other two. It fits when you must do something before you can tell whether to repeat — reading input, then validating it, is the classic case.

for: everything in one line

When you do know the shape of the iteration, for gathers the three pieces together:

#include <iostream>

int main() {
    for (int i = 0; i < 5; ++i) {
        std::cout << i << " ";
    }
    std::cout << "\n";
    return 0;
}
0 1 2 3 4 
for (initialization; condition; update)
  1. Initialization runs once, before anything else.
  2. Condition is checked before each pass; false ends the loop.
  3. Update runs after each pass.

The huge advantage over while is that all three live on one line, so you can see at a glance whether the loop terminates. The variable is also scoped to the loop — i doesn't exist after it, which stops it leaking into the rest of your function.

Counting from zero

C++ is a zero-indexed language, and loops are conventionally written to match:

#include <iostream>

int main() {
    int count = 5;

    for (int i = 0; i < count; ++i) {
        std::cout << i;
    }
    std::cout << "\n";

    for (int i = 1; i <= count; ++i) {
        std::cout << i;
    }
    std::cout << "\n";
    return 0;
}
01234
12345

Both run five times. The first is the idiomatic one, because it matches how you'll index containers from Module 6 onward: valid indices are 0 to size - 1, which is exactly i < size.

The off-by-one rule: < with a zero start, or <= with a one start. Mixing them — i = 0; i <= count — runs one time too many, and when i is an index that's a read past the end of your data.

++i versus i++

Both add one. They differ in what the expression evaluates to: i++ yields the old value, ++i the new one.

#include <iostream>

int main() {
    int a = 5;
    int post = a++;      // post gets the OLD value; a becomes 6

    int b = 5;
    int pre = ++b;       // pre gets the NEW value; b becomes 6

    std::cout << post << " " << a << "\n";
    std::cout << pre << " " << b << "\n";
    return 0;
}
5 6
6 6

(Each increment gets its own statement here on purpose. Writing std::cout << a++ << a — modifying a variable and reading it again in one statement — is the kind of code compilers warn about and readers misread. Don't.)

As a loop's update step the result is discarded, so they behave identically. Prefer ++i anyway: for simple types it makes no difference, but for iterators and heavy objects (Module 13) i++ has to make a copy of the old value to return, and ++i doesn't. It's a free habit.

Nested loops

A loop inside a loop — the inner one runs completely for each pass of the outer:

#include <iostream>

int main() {
    for (int row = 1; row <= 3; ++row) {
        for (int col = 1; col <= 3; ++col) {
            std::cout << row * col;
            if (col < 3) {
                std::cout << " ";
            }
        }
        std::cout << "\n";
    }
    return 0;
}
1 2 3
2 4 6
3 6 9

Nine iterations total. Note the cost: two nested loops over n items is n² work, three is n³. That's fine for 3, and catastrophic for 100,000.

Try it

Write a program that prints the sum of all even numbers from 1 to 20, then prints a right-angled triangle of asterisks 5 rows tall (one on the first row, five on the last).

Checkpoint

  • while checks before, so it can run zero times; do-while checks after, so it runs at least once (and needs a trailing semicolon).
  • for puts initialization, condition, and update on one line and scopes the counter to the loop.
  • Count from 0 with <, matching zero-indexed containers; mixing 0 with <= is the classic off-by-one.
  • Prefer ++i over i++ — identical for ints, cheaper for iterators.
  • Nested loops multiply the work: two levels over n items is n².

Questions about this lesson

Log in to ask a question.