The three loops in C++ — [[for]], [[while]] and [[do while]] — all repeat a block of code, and they differ only in where the loop's bookkeeping lives. The [[for]] loop bundles the counter, the condition and the update in one place, the [[while]] loop tests a condition before the body, and the [[do while]] loop tests it after. Picking the right one is mostly a question of where the state lives.
The for loop keeps its bookkeeping together
A for loop has three parts separated by semicolons: an initialiser, a condition and an update.
#include
int main() {
for (int topping = 1; topping <= 3; ++topping) {
std::cout << "adding topping " << topping << '\n';
}
std::cout << "done\n";
return 0;
}- The initialiser runs once, before the loop.
- The condition is checked before every iteration. If it is false, the loop ends.
- The update runs after every iteration, before the condition is checked again.
The body runs three times, for topping equal to 1, 2 and 3. When the condition becomes false the loop exits and the body never runs again.
The loop variable declared in the initialiser is scoped to the loop and disappears afterwards, which is tidy and prevents it from colliding with another variable of the same name.
The while loop tests first
A while loop has only a condition. Anything else the loop needs — a counter, a search state, a flag — lives outside it.
#include
int main() {
int oven_minutes = 0;
const int target_minutes = 3;
while (oven_minutes < target_minutes) {
std::cout << "baking, minute " << oven_minutes + 1 << '\n';
++oven_minutes;
}
std::cout << "baked for " << oven_minutes << " minutes\n";
return 0;
}The output shows the loop running until the state caught up with the target:
baking, minute 1
baking, minute 2
baking, minute 3
baked for 3 minutesA while loop is the right choice when the number of iterations is not known in advance: reading input until a sentinel value, processing a queue until it is empty, or searching until a match is found.
The do while loop tests last
A do while loop runs the body first and checks the condition afterwards. That means the body always runs at least once, which is exactly what you want when the first action is the thing being validated.
#include
int main() {
int order_number = 0;
do {
std::cout << "entering order number\n";
// in a real program this would come from the keyboard
order_number = 1001;
} while (order_number == 0);
std::cout << "accepted order " << order_number << '\n';
return 0;
}Compare that with a while version of the same idea: with a while loop and an order number of zero, the body would never run and the program would fall straight past the prompt without asking.
Choosing between the three
A quick decision guide.
for— you know roughly how many times you want to iterate, or you are iterating over a range of indices.while— the loop continues until some condition becomes false, and the condition is checked before each pass.do while— the body must run at least once before the condition is worth checking.
The three are interchangeable in power — anything a for loop can do, a while loop can do — so the choice is about readability, not capability.
The infinite loop and how to escape it
If the condition never becomes false, the loop runs forever. In an interactive program the user can usually interrupt it with Ctrl+C, but in a batch job it hangs the process.
#include
int main() {
int attempts = 0;
while (attempts < 3) {
std::cout << "retrying, attempt " << attempts + 1 << '\n';
++attempts;
}
std::cout << "giving up after " << attempts << " attempts\n";
return 0;
}The safe pattern when a loop's condition depends on something that could go wrong is a bounded loop: keep a counter and check it as well.
#include
int main() {
int retries = 0;
bool connected = false;
while (!connected && retries < 5) {
++retries;
connected = (retries >= 2); // pretend the second attempt works
std::cout << "attempt " << retries << (connected ? " connected" : " failed") << '\n';
}
std::cout << "retries used: " << retries << '\n';
return 0;
}The output shows the bounded loop giving up rather than spinning:
attempt 1 failed
attempt 2 connected
retries used: 2Loop scope and the loop variable
The counter declared in a for loop's initialiser lives only for the duration of that loop.
#include
int main() {
for (int i = 0; i < 2; ++i) {
std::cout << "inside: " << i << '\n';
}
// i is out of scope here, so this line would not compile:
// std::cout << i << '\n';
int j = 0;
while (j < 2) {
std::cout << "while j: " << j << '\n';
++j;
}
return 0;
}Referencing i after the loop is a compile error, which is the compiler protecting you from reusing a counter whose value has no meaning any more.
Quick reference
forhas an initialiser, a condition and an update;whilehas only a condition.- The condition is tested before each pass in a
forand awhile. - A
do whiletests after the body, so the body always runs at least once. - All three can express the same computation; pick the one that reads best.
- Give the loop variable a scope of its own by declaring it in the
forinitialiser. - Bound any loop whose exit depends on external input, so a failure cannot hang the program.
- Increment with
++iin the update expression.
Try it yourself: write the same counting loop three times — once with [[for]], once with [[while]] and once with [[do while]] — and confirm the outputs are identical. Then change the [[do while]] version so the condition is false on the first check and observe that it still executes once. Finally, write a loop that counts down from a number the user gives, and make sure it terminates when that number is zero or negative.