Contents

Chapter 8

Loops: Doing It Again Until It's Done

while, for, break, and the runaway loop you will definitely write.

So far every line you write runs once. If you want something ten times, you type it ten times, which you noticed at the end of chapter 5, copying the same four lines with different words in them.

A loop does the repeating for you.

while

int count = 1;

while (count <= 3) {
    std::cout << "count is " << count << '\n';
    ++count;
}
count is 1
count is 2
count is 3

The shape is the same as an if: a condition in brackets, a block in braces. The difference is what happens at the closing brace: instead of carrying on, C++ goes back and checks the condition again. Run, check, run, check, until the condition is false.

That ++count is new, and it’s the whole reason this loop ends.

Adding one

++count means “add one to count”. It’s such a common thing to do that it has its own operator.

++count;      // count = count + 1
count++;      // also count = count + 1

Both work. There’s a difference between them, but it only shows up when you use the result inside a bigger expression, which this book won’t do, because it makes code harder to read for no gain. Prefer ++count and don’t think about it again.

The loop that never ends

Take out the ++count and read what’s left:

int count = 1;

while (count <= 3) {
    std::cout << "count is " << count << '\n';
}

count is 1. One is less than three, so the block runs. Nothing changes count. So one is still less than three. Forever.

Your terminal fills with text and doesn’t stop. Press Ctrl-C. That’s the universal “stop this program”. Hold control and press C, in the terminal window where it’s running. You’ll use it more in your first month than in all the years after.

for

Counting loops all have the same three parts: start somewhere, keep going while something is true, and change something each time round. for puts all three on one line:

for (int i = 0; i < 3; ++i) {
    std::cout << "i is " << i << '\n';
}
i is 0
i is 1
i is 2

Read the brackets as three instructions separated by semicolons:

PartMeaning
int i = 0before the loop starts, make i and set it to 0
i < 3keep looping while this is true
++iafter each pass, do this

It’s exactly the while loop from earlier with the pieces gathered up where you can see them. Nothing new happens, but the thing that makes a loop end is now impossible to leave out by accident, which is why counting loops are almost always written this way.

Note where it starts and stops. i runs 0, 1, 2: three passes, starting at zero, never reaching 3. That’s deliberate: it matches how string positions work from chapter 4, so a loop over name.size() characters lines up without any awkward adjusting.

Leaving early

break gets you out of a loop immediately, wherever you are in it:

for (int i = 0; i < 10; ++i) {
    if (i == 3) {
        break;
    }
    std::cout << "i is " << i << '\n';
}
i is 0
i is 1
i is 2

The loop was told to run ten times and stopped after three. break is how you leave when you’ve found what you were looking for, or when something’s wrong.

There’s also continue, which skips the rest of this pass and starts the next one. It has its uses; in beginner code it usually makes a loop harder to follow than an if would. Worth knowing the word, not worth reaching for yet.

Loops that read input

This is where two chapters collide, and the result is nastier than either alone.

Chapter 5 showed that typing letters into an int leaves it at 0 and jams cin so every later read does nothing. Put that inside a loop:

const int secret = 42;
int guess = 0;

while (guess != secret) {
    std::cout << "Guess: ";
    std::cin >> guess;
    std::cout << "  you said " << guess << '\n';
}

Type abc once and you get this, forever:

Guess:   you said 0
Guess:   you said 0
Guess:   you said 0
Guess:   you said 0

It never pauses for you again. The read fails instantly every time, guess never changes, so the condition never changes. That’s the runaway loop from earlier, with a cause you can’t see by staring at the loop.

Exercise 1 · Write one, then break it

Print the numbers 1 to 10 with a for loop. Then print them backwards, starting at 10, condition i >= 1, and --i to count down.

Now make one on purpose: write the countdown with ++i instead of --i and run it. Watch it climb away from 1 and never stop, and practise Ctrl-C while the stakes are zero.

Check yourself

1. A while loop prints forever. What is always true about it?

Yes. If the condition depends on nothing that changes, its answer can never change. That is every infinite loop, without exception.

Not quite. Sometimes, but not always, and a backwards condition usually means the loop runs zero times rather than forever.

Not quite. No such requirement. Plenty of correct loops run until something happens rather than a fixed number of times.

2. How many times does for (int i = 0; i < 3; ++i) run its block?

Not quite. When i reaches 3 the condition i < 3 is already false, so the block does not run that time.

Yes. Starting at zero and stopping before the limit, which lines up with how string positions are numbered.

Not quite. It starts at whatever you set it to, and here that is 0. The values are 0, 1, 2.

3. An input loop suddenly stops waiting and repeats forever. What happened?

Not quite. The loop is usually fine. Something upstream stopped supplying values to it.

Yes. Every read after that does nothing, the variable never changes, and the condition never changes. Ctrl-C, and chapter 17 fixes it properly.

Not quite. It has not crashed. It is running enthusiastically. And running out of input is a different situation, which ends a while (std::cin >> x) loop cleanly.

Project

The number guessing game

Roughly 45 minutes

The program picks a number. You guess. It tells you higher or lower. It counts how many tries you took.

Your guess: 10
Higher.
Your guess: 80
Lower.
Your guess: 42
Got it in 3 attempts.

What it needs:

  • a const int secret, hardcoded for now, say 42
  • a guess and an attempts counter, both starting at 0
  • a while loop that keeps going until the guess matches
  • an if / else if inside it for higher and lower

Two things to get right. The counter goes up on every guess including the last one, so increment it right after reading. And there’s no else telling them they were correct inside the loop. The loop ending is what means they got it, so that message belongs after the closing brace.

Stretch one, a real random number. Hardcoding a secret makes for a short game. This picks one between 1 and 100:

#include <random>

std::random_device seed;
std::mt19937 generator(seed());
std::uniform_int_distribution<int> between(1, 100);

const int secret = between(generator);

Borrowed machinery, like setprecision in chapter 6. Copy it and carry on. Three lines, and every run is a different game.

Stretch two, limit the guesses. Give them seven tries. If they run out, tell them the answer. This is what break is for, and you’ll need to work out afterwards whether they won or lost, which is harder than it sounds.