Why this matters
Real loops rarely run cleanly from start to finish. A search stops as soon as it finds what it is looking for. Input processing skips blank or invalid records. A grid or a table needs one loop inside another. This lesson covers the two statements that alter a loop's flow, break and continue, how loops nest, and a handful of patterns (accumulators, flags, sentinels, running minimum and maximum) that appear in almost every program you will write.
break: leave the loop now
break immediately exits the innermost enclosing loop (or switch). Execution continues with the first statement after that loop.
int n = 91;
int divisor = 0;
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
divisor = d;
break; /* found one: no point testing further */
}
}
if (divisor)
printf("%d = %d x %d\n", n, divisor, n / divisor);
else
printf("%d is prime\n", n);
91 = 7 x 13
Without break, the loop would keep testing after the answer was known. In a search over a million items, stopping early is the difference between fast and slow.
continue: skip to the next pass
continue abandons the rest of the current pass and goes on to the next one. In a while or do-while it jumps to the condition test; in a for it jumps to the update expression first and then to the test.
for (int i = 1; i <= 10; i++) {
if (i % 3 == 0)
continue; /* skip multiples of 3 */
printf("%d ", i);
}
printf("\n");
1 2 4 5 7 8 10
for (init; cond; update) { while (cond) {
... ...
continue; --+ continue; --+
... | ... |
} | } |
goes to update, then cond goes straight to cond
That difference is the one place where a for loop is not identical to its while rewrite: in the while version, the update at the bottom of the body is skipped by continue, which can create an infinite loop:
int i = 0;
while (i < 10) {
if (i % 3 == 0)
continue; /* BUG: i++ below is skipped, i stays 0 forever */
printf("%d ", i);
i++;
}
continue shines as a guard at the top of a loop body: "if this item is not interesting, move on". It keeps the main logic unindented instead of wrapping it in a large if.
Nested loops
A loop body can contain another loop. The inner loop runs completely for each pass of the outer loop:
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 4; col++)
printf("%4d", row * col);
printf("\n");
}
1 2 3 4
2 4 6 8
3 6 9 12
The inner body runs 3 x 4 = 12 times. Nesting multiplies work: two nested loops over n items do n squared steps, three do n cubed. For n = 1000 that is a million versus a billion steps, which is why the shape of your loops matters more than micro-optimizations.
break only leaves one level
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1)
break; /* leaves the j loop only */
printf("(%d,%d) ", i, j);
}
}
(0,0) (1,0) (2,0)
To stop both loops, the usual tools are a flag variable tested by the outer loop:
int found = 0;
for (int a = 1; a < 20 && !found; a++) {
for (int b = a; b < 20; b++) {
if (a * a + b * b == 13 * 13) {
printf("%d^2 + %d^2 = 13^2\n", a, b);
found = 1;
break;
}
}
}
5^2 + 12^2 = 13^2
or moving the loops into their own function and using return (functions are the next module), or goto:
for (...) {
for (...) {
if (done)
goto out;
}
}
out:
...
goto jumps to a label in the same function. Its reputation is poor because unrestricted jumping makes code impossible to follow, but this one use, escaping nested loops or jumping forward to a cleanup block, is accepted style in C and common in the Linux kernel.
Loop patterns
Most loops are one of a few patterns. Recognizing them lets you write loops quickly and correctly.
Counter: how many items satisfy a condition.
int evens = 0;
for (int i = 1; i <= 20; i++)
if (i % 2 == 0)
evens++;
Accumulator: combine every item into one value (sum, product). Start at the identity: 0 for a sum, 1 for a product.
long product = 1;
for (int i = 1; i <= 10; i++)
product *= i; /* 10! = 3628800 */
Search with early exit: the prime test above. A flag records whether you found it.
Running minimum and maximum: initialize from the first item, not from an arbitrary constant. If you start min at 0 and all inputs are positive, min stays 0 forever.
if (count == 0 || x < min)
min = x;
Sentinel-controlled input: keep reading until a special value that is not data (like 0 or -1) or the end of input:
while (scanf("%d", &x) == 1) {
if (x == 0)
break; /* sentinel */
...
}
Checking scanf's return value inside the loop condition means the loop also stops cleanly at end of input or on non-numeric input. The next lesson builds on exactly this.
Common mistakes
- Expecting
breakto exit all loops. It exits one. Use a flag, a function return, orgoto. breakinside aswitchinside a loop. It leaves the switch, not the loop. If you need to leave the loop from a case, use a flag orgoto.continuein awhileloop skipping the update, causing an infinite loop. Put the update before thecontinue, or use aforloop.- Bad initial values: min/max starting at 0, a product starting at 0, a counter not reset between passes of an outer loop. Ask: what should the variable be before the first item?
- Using the inner loop's variable in the outer loop, for example both loops using
i. In C99 style eachfor (int i ...)declares a newi, so the inner one hides the outer one; the outer loop still works, but code inside the inner loop cannot see the outeri. Give them different names.
Recap
breakexits the innermost loop or switch;continueskips to the next pass (in afor, via the update).- Nested loops multiply the work: the inner loop runs fully for each outer pass.
- To leave several loops at once, use a flag, a
return, or a forwardgoto. - Recurring patterns: counter, accumulator (start at the identity), search with early exit, running min/max (start from the first item), sentinel-controlled input.
Going further: write a program that prints all primes below 100 using a nested loop and break. Then count how many times the inner loop body runs, and see how much the d * d <= n bound saves compared with testing every d < n.