C++ / CONTROL FLOW
break, continue, and early exits from loops
Skip or abandon loop iterations deliberately: use continue and break with the right target, and leave nested loops with a return instead of flags.
What you will learn
- Use continue to skip one iteration; in a for loop the iteration expression still runs
- Know that break leaves only the innermost loop or switch, never two levels at once
- Exit nested loops by extracting them into a function and returning
- Record a search result in a variable, because C++ has no loop-else construct
Understanding break, continue, and early exits from loops
break and continue are jump statements: they do not test anything, they move control to a fixed place. continue is the one people mispicture. It does not jump back to the top of the loop body; it jumps to just past the body's last statement, where the loop's per-iteration machinery lives. In a for loop that machinery is the iteration expression in the head, so ++i still runs after a continue. A while loop has no such head, so any advance the body was going to perform is skipped along with everything else, and the condition is re-tested against an unchanged value.
The target of both statements is decided purely by lexical nesting: the innermost enclosing iteration statement, and for break also the innermost enclosing switch. That single rule explains two behaviours that surprise beginners. A break inside a switch that sits inside a loop ends the switch and lets the loop continue, and a break in the inner of two nested loops leaves the outer loop iterating. C++ has no labelled break or continue, so escaping more than one level is a design decision rather than a syntax trick.
break stops iterating, but the function keeps going: the statements after the loop run normally, which is exactly where you inspect what happened. return is the stronger early exit, abandoning the whole function, and it is safe in C++ because destructors for local objects run on every exit path, including an early return from deep inside nested loops. One consequence of break is worth internalising: after it fires, the loop condition was still true, so you cannot use the condition to distinguish "found it" from "ran out of elements" and must record the outcome yourself, or use an algorithm such as std::find_if that has the break built in.
placeholder
<cstddef>
<iostream>
<vector>
int main() {
std::vector<int> readings = {12, -1, 7, -3, 0, 9, 4};
int sum = 0;
int used = 0;
for (std::size_t i = 0; i < readings.size(); ++i) {
int r = readings[i];
if (r < 0) {
std::cout << "skip index " << i << " (bad reading)\n";
continue; // jumps to ++i, not to the top of the body
}
if (r == 0) {
std::cout << "stop at index " << i << " (end marker)\n";
break; // the loop condition is never tested again
}
sum += r;
++used;
}
std::cout << "sum=" << sum << " used=" << used << "\n";
}The target of break and continue is fixed by lexical nesting: break jumps past the end of the innermost loop or switch, while continue jumps to that loop's next iteration step rather than back to the top of its body.
Worked examples
break inside a switch does not leave the loop
Shows that break binds to the innermost switch, so the surrounding loop keeps iterating.
<iostream>
int main() {
const char* cmd = "aqbz";
for (int i = 0; cmd[i] != '\0'; ++i) {
switch (cmd[i]) {
case 'q':
std::cout << "quit requested\n";
break; // leaves the switch only
default:
std::cout << "handling '" << cmd[i] << "'\n";
break;
}
std::cout << " loop body finished\n";
}
}Example explained
Line 1The break in case 'q' matches the switch, the nearest enclosing breakable statement, so control resumes at the statement after the switch's closing brace.
Line 2The "loop body finished" line printed right after "quit requested" is the proof: the loop was never interrupted.
Line 3Characters 'b' and 'z' are still processed afterwards, so the quit request has no effect on iteration at all.
Line 4To actually stop here you need a bool flag tested in the loop condition, or a return from the enclosing function.
continue and the advance step
Compares where continue lands in a while loop versus a for loop, and why the for loop is safer.
<iostream>
int main() {
int n = 0;
while (n < 6) {
int value = n;
++n; // advance before any continue
if (value % 2 == 0) {
continue; // jumps straight to the n < 6 test
}
std::cout << "odd value " << value << "\n";
}
for (int i = 0; i < 6; ++i) {
if (i % 2 == 0) {
continue; // ++i still runs, so this cannot hang
}
std::cout << "odd i " << i << "\n";
}
}Example explained
Line 1++n sits above the continue, so every path through the while body advances the counter and the loop can terminate.
Line 2The continue in the while loop skips everything down to the closing brace and then evaluates n < 6 again; the condition is re-tested, just with whatever value n now holds.
Line 3In the for loop the same continue lands on ++i, because the iteration expression belongs to the loop head and is not part of the body.
Line 4Both loops print the same numbers, but only the for loop is immune to a forgotten or unreachable increment.
return as a multi-level exit
Leaves two nested loops at once with return, and shows that destructors still run on that path.
<iostream>
<string>
struct Trace {
std::string name;
~Trace() { std::cout << "cleanup " << name << "\n"; }
};
bool findValue(const int grid[3][3], int target, int& row, int& col) {
Trace t{"findValue"};
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
if (grid[r][c] == target) {
row = r;
col = c;
return true; // leaves both loops and the function
}
}
}
return false;
}
int main() {
const int grid[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int row = -1;
int col = -1;
if (findValue(grid, 5, row, col)) {
std::cout << "found at " << row << "," << col << "\n";
}
}Example explained
Line 1return true exits the inner loop, the outer loop and the function in one step, which no single break can do.
Line 2The Trace destructor runs on that path, so "cleanup findValue" prints before "found at 1,1": an early return does not skip RAII cleanup.
Line 3return false is reachable only when both loops finish, so the not-found answer comes from the control flow instead of a found flag.
Line 4Because the loops now live in their own function, the caller reads as a single condition rather than a flag check after a loop.
Important notes
In a do-while, continue jumps to the condition test at the bottom, so a continue near the top of the body can end the loop rather than repeat it.
Only loops and switch absorb these jumps; an if or a bare block does not, so a continue written inside an if inside a loop still belongs to that loop.
Common mistakes
Placing continue above the ++i at the bottom of a while body: the counter never changes, the condition stays true, and the program hangs instead of skipping one item.
Using break inside a switch that sits in a loop to stop looping: it only ends the switch, so the loop runs to completion and the intended quit never happens.
Reading the loop counter after the loop to see where the break fired: for (int i = 0; ...) scopes i to the loop, so the code either fails to compile or silently reads a different, outer i.
placeholder
Try it yourself
Change, predict, then run
Loop over int a[] = {4, 15, 9, 0, 7, 2}, using continue to skip any value above 10 and break at the first 0, accumulating a sum as you go. Print the sum and the index where you stopped; you should get 13 and 3.
Open the C++ workspaceCheck your understanding
A while loop increments its counter on the last line of the body. You insert a continue in the middle to skip unwanted items and the program now hangs. What is happening?
- continue skips the rest of the body, including the increment, so the condition keeps seeing the same counter value
- continue restarts the body from its first statement without re-evaluating the loop condition, so the loop can never end
- continue is only meaningful in for loops; in a while loop it repeats the current statement instead of advancing
- continue discards the condition's cached result, so the loop can only be left by a break or a return
Show answer
continue transfers control past the last statement of the body, so the increment on that last line never executes and the condition is tested again with an unchanged counter. Option 2 is tempting but wrong on the mechanism: the condition is still evaluated on every pass, it just evaluates to true forever. The same continue in a for loop is safe because the increment lives in the loop head, which continue runs on its way to the next iteration.