Loops
So far every statement you wrote ran once, or not at all. A loop runs the same statements many times. This lesson shows the three loops of C++, how to count the number of times each one runs, and how to follow a loop on paper, one row of a table at a time.
The same statement, many times
This program draws five circles in a row.
#include "scimigo.h"
using namespace scimigo;
int main() {
canvas(600, 400);
circle(100, 200, 30, "teal");
circle(200, 200, 30, "teal");
circle(300, 200, 30, "teal");
circle(400, 200, 30, "teal");
circle(500, 200, 30, "teal");
}
The five statements differ in one value only: x is 100, then 200, 300, 400, 500. Each x is 100 more than the one before.
Writing them out works for five circles. It does not work for five hundred, and it does not work when the number of circles is known only while the program runs.
Put x in a variable, and the difference between the statements disappears.
int x = 100;
circle(x, 200, 30, "teal");
x = x + 100;
circle(x, 200, 30, "teal");
x = x + 100;
Now the same two statements come again and again: draw a circle at x, then add 100 to x. Statements that repeat exactly are what a loop is for. You write them once and say how long to keep going.
while
The first loop is while. It looks like an if, and it starts like one.
#include <iostream>
int main() {
int n = 3;
while (n > 0) {
std::cout << n << "\n";
n = n - 1;
}
std::cout << "Lift off\n";
}
3
2
1
Lift off
The statements between the braces are the loop's body. This is what the computer does:
- It works out the condition
n > 0. This is the test. - If the test is true, it runs the whole body, and then goes back to step 1.
- If the test is false, it skips the body and carries on after the closing brace.
An if does steps 1 and 3 and never goes back. Going back is the only difference.
Each run of the body is one turn of the loop. To follow a loop on paper, write a table with one row for each time the test is worked out.
| Test number | n | n > 0 |
Printed | n after the body |
|---|---|---|---|---|
| 1 | 3 | true | 3 |
2 |
| 2 | 2 | true | 2 |
1 |
| 3 | 1 | true | 1 |
0 |
| 4 | 0 | false | nothing | the loop is over |
Notice two things.
- The test is worked out four times, and the body runs three times. The last test is the one that comes out false.
- The test is looked at only at the top. When
nbecomes 0 in the middle of the third turn, nothing happens yet. The loop ends when the computer goes back to the test and finds it false.
Here are the five circles again, drawn by a loop.
#include "scimigo.h"
using namespace scimigo;
int main() {
canvas(600, 400);
int x = 100;
while (x <= 500) {
circle(x, 200, 30, "teal");
x = x + 100;
}
}
There is one circle statement, and it runs five times, with x equal to 100, 200, 300, 400 and 500. Then x becomes 600, the test x <= 500 is false, and the loop is over.
Start, test, update
A loop that counts has three parts, and each one is a place where it can go wrong.
#include <iostream>
int main() {
int count = 0; // the start value
while (count < 3) { // the test
std::cout << "knock\n";
count = count + 1; // the update
}
std::cout << count << "\n";
}
knock
knock
knock
3
- The start value is given once, before the loop.
- The test is worked out before every turn.
- The update changes the variable, so that the test will one day be false.
The last line prints 3. After a loop, its test is false: here count < 3 is false, because count is 3.
Now think about this program without its update line. count would stay 0, the test 0 < 3 would be true every time, and the program would print knock without end. A loop whose test never becomes false is an infinite loop. The compiler does not notice it, because every statement is correct C++. The lab stops a program that runs for too long and tells you so. When you see that message, look for the update first.
Shorter spellings
Changing a variable by a fixed amount is so common that C++ has shorter ways to write it.
| Long form | Shorter | Shortest |
|---|---|---|
i = i + 1; |
i += 1; |
++i; |
i = i - 1; |
i -= 1; |
--i; |
i = i + 5; |
i += 5; |
|
total = total + price; |
total += price; |
Each row is one meaning written in different ways: read the variable, work out the new value, store it back.
#include <iostream>
int main() {
int i = 10;
++i;
std::cout << i << "\n";
i += 5;
std::cout << i << "\n";
i -= 6;
std::cout << i << "\n";
--i;
std::cout << i << "\n";
}
11
16
10
9
You will also see i++. As a statement of its own it does the same as ++i. This course writes ++i.
for
The three parts of a counting loop are easy to lose: the start value sits above the loop and the update is at the bottom of the body. A for loop puts all three on one line.
This program has the same loop twice, first as a while and then as a for.
#include <iostream>
int main() {
int i = 1;
while (i <= 3) {
std::cout << "Lap " << i << "\n";
++i;
}
for (int k = 1; k <= 3; ++k) {
std::cout << "Lap " << k << "\n";
}
}
Lap 1
Lap 2
Lap 3
Lap 1
Lap 2
Lap 3
Inside the parentheses of a for are three parts, separated by two semicolons.
| Part | In this loop | When it runs |
|---|---|---|
| start | int k = 1 |
once, before anything else |
| test | k <= 3 |
before every turn |
| update | ++k |
after every turn of the body |
The update is written on the first line, but it runs after the body, exactly where ++i; stands in the while loop. Here is the for loop traced by hand.
| Turn | k | k <= 3 |
Printed | k after the update |
|---|---|---|---|---|
| 1 | 1 | true | Lap 1 |
2 |
| 2 | 2 | true | Lap 2 |
3 |
| 3 | 3 | true | Lap 3 |
4 |
| 4 | false | nothing | the loop is over |
A variable declared in the parentheses of a for belongs to that loop. It ends at the loop's closing brace, by the rule you know from module 4. This program does not compile, because its last statement uses k after the loop.
#include <iostream>
int main() {
for (int k = 1; k <= 3; ++k) {
std::cout << "Lap " << k << "\n";
}
std::cout << k << "\n";
}
Using the counter
The counter is an ordinary variable, and the body can calculate with it. That is how one statement draws different things on different turns.
#include "scimigo.h"
using namespace scimigo;
int main() {
canvas(600, 400);
for (int i = 0; i < 6; ++i) {
rectangle(40 + i * 90, 170, 60, 60, "teal");
}
}
| i | 40 + i * 90 |
|---|---|
| 0 | 40 |
| 1 | 130 |
| 2 | 220 |
| 5 | 490 |
The first square is at the start position, 40, because i starts at 0 and 0 * 90 is 0. Each turn moves the next square 90 to the right.
Use for when you can say the start, the test and the update in one breath: "for i from 0 up to 6". Use while when you cannot.
Ten or eleven?
How many turns does a loop make? The answer is in the start value and the test, and one character changes it.
#include <iostream>
int main() {
int turns = 0;
for (int i = 0; i < 5; ++i) {
++turns;
}
std::cout << turns << "\n";
turns = 0;
for (int i = 0; i <= 5; ++i) {
++turns;
}
std::cout << turns << "\n";
turns = 0;
for (int i = 1; i <= 5; ++i) {
++turns;
}
std::cout << turns << "\n";
}
5
6
5
To count the turns of any loop, write down the values the counter takes.
| Loop | Values of i in the body | Turns |
|---|---|---|
for (int i = 0; i < 5; ++i) |
0, 1, 2, 3, 4 | 5 |
for (int i = 0; i <= 5; ++i) |
0, 1, 2, 3, 4, 5 | 6 |
for (int i = 1; i <= 5; ++i) |
1, 2, 3, 4, 5 | 5 |
A loop that makes one turn too many or one too few has an off-by-one error. It is the most common mistake in loops, and the program still compiles and runs.
Two forms are worth knowing by heart, because each makes exactly n turns.
for (int i = 0; i < n; ++i)counts 0, 1, up ton - 1. Use it when the value is used to calculate a position, as the squares above do.for (int i = 1; i <= n; ++i)counts 1, 2, up ton. Use it when the values themselves are wanted, as in "print 1 to n".
When you are unsure, ask two questions: what is i on the first turn, and what is i on the last turn?
Adding up as you go
A loop can build an answer one turn at a time. This program adds the odd numbers from 1 to 9.
#include <iostream>
int main() {
int total = 0;
for (int i = 1; i <= 9; i += 2) {
total += i;
}
std::cout << total << "\n";
}
25
The pattern has two statements, and their places matter.
- Before the loop, the total gets its start value:
int total = 0;. - Inside the loop, something is added to it:
total += i;.
| Turn | i | total after the turn |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3 | 4 |
| 3 | 5 | 9 |
| 4 | 7 | 16 |
| 5 | 9 | 25 |
The total is kept from one turn to the next. That only works because its first statement is outside the loop. In the next program a line total = 0; has been put inside the body.
#include <iostream>
int main() {
int total = 0;
for (int i = 1; i <= 9; i += 2) {
total = 0;
total += i;
}
std::cout << total << "\n";
}
9
Every turn now begins by throwing away what the turns before it added. Only the last turn's 9 is left.
Counting is the same pattern with ++count; in the body: start at 0 before the loop, add one inside it.
A loop inside a loop
The body of a loop can hold any statements, and a loop is a statement. A loop inside another loop is a nested loop.
#include <iostream>
int main() {
for (int shelf = 1; shelf <= 3; ++shelf) {
for (int box = 1; box <= 2; ++box) {
std::cout << "shelf " << shelf << ", box " << box << "\n";
}
}
}
shelf 1, box 1
shelf 1, box 2
shelf 2, box 1
shelf 2, box 2
shelf 3, box 1
shelf 3, box 2
The inner loop is the whole body of the outer loop. So on each turn of the outer loop, the inner loop runs from its start to its end. box goes 1, 2 and the inner loop is over. Only then does shelf move on, and the inner loop begins again from int box = 1.
The inner variable changes fastest. The line is printed 3 times 2, which is 6 times.
Two loops give two numbers to draw with: one for the position across, one for the position down.
#include "scimigo.h"
using namespace scimigo;
int main() {
canvas(600, 400);
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 5; ++col) {
circle(100 + col * 100, 100 + row * 100, 30, "teal");
}
}
}
col decides x, so a larger col is further right. row decides y, so a larger row is lower. The picture is drawn one row at a time, each row from left to right. Swap row and col inside the circle call and the program draws five rows of three.
The inner loop's test may use the outer loop's variable. Then the inner loop makes a different number of turns each time.
#include <iostream>
int main() {
for (int row = 1; row <= 4; ++row) {
for (int k = 1; k <= row; ++k) {
std::cout << k;
}
std::cout << "\n";
}
}
1
12
123
1234
On the outer loop's first turn row is 1, and the inner loop makes one turn. On its fourth turn row is 4, and the inner loop makes four. The statement std::cout << "\n"; is in the outer loop but not in the inner one, so it runs once for each row.
Leaving early and skipping
Two statements change the course of a loop from inside its body.
break; leaves the loop at once. The rest of the body is skipped, the test is not worked out again, and the program carries on after the loop's closing brace.
#include <iostream>
int main() {
for (int i = 1; i <= 100; ++i) {
if (i * i > 200) {
std::cout << i << "\n";
break;
}
}
}
15
The header says "up to 100", but the loop stops at 15, the first number whose square is more than 200. Nothing is printed for 16 or after.
continue; skips the rest of this turn only. The loop itself goes on: a for loop runs its update next, and then its test.
#include <iostream>
int main() {
for (int i = 1; i <= 8; ++i) {
if (i % 3 == 0) {
continue;
}
std::cout << i;
}
std::cout << "\n";
}
124578
For 3 and 6 the continue runs, so the output statement below it does not.
Both statements act on the loop they are written in. Inside a nested loop, break leaves the inner loop only.
Take care with continue in a while loop. If it comes before the update, the update is skipped too, the variable never changes, and the loop is infinite.
Remembering what the loop found
Often a loop searches for something, and the program needs to know afterwards whether it was found. A bool set before the loop holds the answer.
#include <iostream>
int main() {
int n = 144;
bool is_square = false;
for (int i = 1; i <= n; ++i) {
if (i * i == n) {
is_square = true;
break;
}
}
if (is_square) {
std::cout << n << " is a square\n";
} else {
std::cout << n << " is not a square\n";
}
}
144 is a square
The bool starts with the answer for "nothing found". The loop changes it at most once, and then leaves, because one find settles the question. The answer is printed after the loop, never inside it: inside, the program does not yet know.
Test at the end
A while loop tests first, so its body may run zero times. Sometimes the body must run once before there is anything to test. The do-while loop tests at the end.
#include <iostream>
int main() {
int n = 0;
do {
std::cout << "n is " << n << "\n";
--n;
} while (n > 0);
std::cout << "after\n";
}
n is 0
after
The body runs, and then the test n > 0 is worked out. It is false, so the loop ends after one turn. A while (n > 0) loop with the same body would print no n is line at all.
Two details of the spelling: the test comes after the closing brace, and the line ends with a semicolon.
The usual job for this loop is asking again. The question has to be asked once before an answer exists.
#include <iostream>
int main() {
int answer = 0;
do {
std::cout << "What is 6 * 7?\n";
std::cin >> answer;
} while (answer != 42);
std::cout << "Correct\n";
}
36 48 42
What is 6 * 7?
What is 6 * 7?
What is 6 * 7?
Correct
answer is declared before do, not inside the braces. A variable declared inside the braces ends at the closing brace, and the test comes after that brace.
Until the input runs out
In module 4 you tested one read: if (std::cin >> x) is true only when the read worked. Put the same read in a while, and the loop goes on for as long as there is another value.
#include <iostream>
int main() {
double price = 0;
double total = 0;
int items = 0;
while (std::cin >> price) {
total += price;
++items;
}
std::cout << "Items: " << items << "\n";
std::cout << "Total: " << total << "\n";
}
2.5 4 1
Items: 3
Total: 7.5
The test of this loop does two things: it reads the next value into price, and it answers whether that worked.
| Test number | The read | Worked? | total after the turn | items |
|---|---|---|---|---|
| 1 | 2.5 | true | 2.5 | 1 |
| 2 | 4 | true | 6.5 | 2 |
| 3 | 1 | true | 7.5 | 3 |
| 4 | nothing is left | false | the loop is over |
The program does not know in advance how many values there are, and it does not need to. In the lab, the input is what is in the Input box, and it runs out when the last value there has been read. The values can be on one line or on several.
A read also fails when the next thing in the input is not a value of the right type. With the input 2.5 4 one 1 this loop would stop at one, with two items read. It does not skip the word and go on to the 1.
With no input at all, the first test is already false and the body never runs. The program prints Items: 0. If your program divides by the number of values it read, that is the case to think about before you divide.