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)
- Initialization runs once, before anything else.
- Condition is checked before each pass; false ends the loop.
- 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
whilechecks before, so it can run zero times;do-whilechecks after, so it runs at least once (and needs a trailing semicolon).forputs initialization, condition, and update on one line and scopes the counter to the loop.- Count from 0 with
<, matching zero-indexed containers; mixing0with<=is the classic off-by-one. - Prefer
++ioveri++— identical for ints, cheaper for iterators. - Nested loops multiply the work: two levels over
nitems isn².
Questions about this lesson
Log in to ask a question.