Skip to content
VLSI Mentor

C++ Part I · Module 5 · Control Flow

break, continue and Loop Correctness

Every loop in this module has run its body from top to bottom and tested its condition at the appointed moment. Real loops often want neither. Sometimes the answer is found halfway through the body and there is no reason to finish, let alone iterate again; sometimes one item should be passed over without ending the traversal. C++ has a statement for each, and both are easy to use correctly once you know the one thing that is not obvious about them. That one thing is where this chapter earns its place. Chapter 5.4 established that a loop is a condition plus a guarantee that the body makes progress toward the condition becoming false, and continue is the statement that can quietly void the second half of that. In a for loop it is harmless, because the advance lives in the header and runs regardless. In a while loop the advance is an ordinary statement in the body, and skipping it means skipping the only thing that was going to end the loop. The chapter then closes the module on the two bugs that account for most loop failures, and on a measurement that is worth knowing for its own sake: whether your compiler warns about one of the most common of them turns out to depend on which line you put a semicolon on.

Foundation17 min readC++Part IFundamentalsControl Flow

Part I · Module 5 · Chapter 5.7 · break, continue and Loop Correctness

1. Two Statements That Change the Normal Flow

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    break;        // leave the loop entirely
    continue;     // abandon this iteration, go on to the next

Both are single words with no operands, and both act on the innermost enclosing loop.

They exist because the natural shape of a loop — test, run the whole body, test again — is not always the shape of the problem. A search knows the answer before the collection runs out. A filter has nothing to do with some items. Writing either of those without break or continue is possible and usually involves an extra flag variable that exists only to carry the decision to the bottom of the body.

2. break: Leave Now

break ends the loop immediately. Nothing else in the body runs, the condition is not re-tested, and execution continues after the loop:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    for (int i = 0; i < n; ++i) {
        if (match(i)) {
            found = i;
            break;              // no reason to look at the rest
        }
    }

Chapter 5.4 §7 already relied on this: for (;;) with a break is the well-defined way to write a deliberately unbounded loop, and the break is what gives it an exit.

break leaves exactly one loop. In nested loops it is the inner one:

Azvya Education Pvt. Ltd.VLSI Mentor
innermost.cpp — complete program
#include <iostream>

int main()
{
    for (int r = 0; r < 3; ++r) {
        for (int c = 0; c < 3; ++c) {
            if (c == 1) break;          // leaves the inner loop only
            std::cout << "r" << r << "c" << c << ' ';
        }
    }
    std::cout << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
r0c0 r1c0 r2c0

The inner loop was cut short three separate times — once per outer iteration — and the outer loop ran to completion. There is no break 2; in C++. To leave both, the usual answers are a flag tested by the outer condition, or moving the nested loop into a function and using return, which Chapter 6.3 makes the cleaner option.

A reminder from Chapter 5.3: inside a switch, break ends the switch, not an enclosing loop. A switch inside a loop therefore needs care — the break that ends a case does not continue the loop's iteration or leave it.

3. continue: Skip the Rest of This Iteration

continue abandons the remainder of the body and goes on to the next iteration:

Azvya Education Pvt. Ltd.VLSI Mentor
skip.cpp — complete program
#include <iostream>

int main()
{
    for (int i = 0; i < 5; ++i) {
        if (i == 2) continue;          // the ++i still happens
        std::cout << "i = " << i << '\n';
    }
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
i = 0
i = 1
i = 3
i = 4

i == 2 was skipped and the loop finished normally. That comment is the most important one in the chapter: in a for loop, continue still runs the advance, because the advance is in the header and the header is not part of the body.

4. The Same continue in a while Loop Never Ends

Now put the identical logic in a while loop, where the advance is an ordinary statement:

Azvya Education Pvt. Ltd.VLSI Mentor
stuck.cpp — complete program. The guard is the only thing that stops it.
#include <iostream>

int main()
{
    int i = 0, guard = 0;
    while (i < 5) {
        if (i == 2) {
            if (++guard > 3) {
                std::cout << "stuck at i=" << i << ", stopped by a guard\n";
                break;
            }
            continue;                  // skips ++i -- nothing advances
        }
        std::cout << "i = " << i << '\n';
        ++i;
    }
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
i = 0
i = 1
stuck at i=2, stopped by a guard

Without the guard this loop runs forever. i reaches 2, the continue jumps straight back to the condition, i is still 2, the condition is still true, and the body makes the same decision again. Nothing in the program ever changes.

This is not Chapter 5.4's undefined kind of infinite loop. The body does observable work — it prints — so the forward-progress assumption does not apply and the behaviour is perfectly defined. It simply never finishes.

Chapter 5.4 §4 asked three questions of every loop. The second one was "does the progress statement always run?" — and continue is the statement that makes the answer no.

Three ways to write it correctly, in the order you should prefer them:

Use a for loop. The advance moves into the header, where continue cannot skip it. This is the strongest argument for preferring for whenever the stepping is regular.

Advance before the continue.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    while (i < 5) {
        const int current = i;
        ++i;                           // advance first, unconditionally
        if (current == 2) continue;
        std::cout << "i = " << current << '\n';
    }

Invert the condition instead of continuing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    while (i < 5) {
        if (i != 2) std::cout << "i = " << i << '\n';
        ++i;
    }

The third is usually the clearest for a single skip. continue earns its place when the body is long and several conditions each disqualify an item — turning one deeply nested if into a short series of guard clauses at the top.

5. continue in a do-while Goes to the Condition

One more behaviour, because it surprises people and because the surprise is not what they expect:

Azvya Education Pvt. Ltd.VLSI Mentor
dowhile.cpp — complete program
#include <iostream>

int main()
{
    int i = 0, body_entries = 0;
    do {
        ++body_entries;
        ++i;
        if (i < 3) continue;           // jumps straight to the while(...) test
        std::cout << "reached the tail at i = " << i << '\n';
    } while (i < 5);
    std::cout << "body entered " << body_entries << " times, final i = " << i << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reached the tail at i = 3
reached the tail at i = 4
reached the tail at i = 5
body entered 5 times, final i = 5

The continue does not restart the body — it jumps to the condition at the bottom, which is then tested as normal. So the loop terminated correctly here: the advance was before the continue, which is §4's second remedy applied by accident.

What it did do is skip the tail of the body on the first two iterations. The body was entered five times and the tail was reached three. In a do-while the condition is below the code it governs, so continue jumps forward rather than back, and a reader scanning for "where does continue go?" has to look down instead of up.

This is one more reason Chapter 5.4 §3 asked you to use do-while rarely.

C++ does have one more jump statement, goto, which can leave several loops at once. It is mentioned here only so you recognise it; essentially every use of it in modern C++ is better written another way, and nothing in this curriculum needs it.

6. The First Bug: Off-By-One

The two bugs this chapter closes on account for most loop failures, and both have already been measured in this module.

Off-by-one is a loop that runs one iteration too many or too few. Chapter 5.5 §5 measured the plain version: i <= n runs n + 1 times where i < n runs n.

The reliable fix is the convention from that chapter rather than care: start at zero, stop before the count. The half-open range makes the length equal the bound, so there is no + 1 or - 1 anywhere to get backwards.

Two habits catch the rest:

Check the ends, never the middle. For a loop over n items, trace n = 0, n = 1, and the last iteration. The middles always work. A loop that is right at zero, right at one, and right at the top is right.

Count the iterations, not the comparisons. Ask "how many times does the body run?" and compare it to the number you wanted. That is a question with one answer, where "is <= right here?" is a question about punctuation.

The worst instance in this module was Chapter 5.5 §6's countdown, where the off-by-one at the bottom end did not produce one extra iteration — it produced 4294967295 and a loop that could not terminate, because the index was unsigned.

7. The Second Bug: The Loop That Never Ends

Three distinct causes have appeared, and they are worth separating because the fixes differ:

CauseWhereFix
No progress statement at all5.4 §5write one; and note it was undefined, trapping at -O2
The condition can never be false5.5 §6i >= 0 on an unsigned index — use a signed type
The progress statement is skipped§4 aboveuse for, or advance before the continue

One checklist covers all three, and it is Chapter 5.4 §4's, with the third item now sharpened:

  1. Name the statement that makes the condition false. If you cannot, stop writing and fix that.
  2. Can the condition be false at all? An unsigned value is never below zero.
  3. Does that statement run on every path through the body? Check every continue and every early branch.

8. Where the Compiler Will and Will Not Help

Three mistakes, three different answers — and the third is the one worth remembering.

A stray semicolon after the header. The loop gets an empty body and the code you meant as the body runs once, after the loop:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    for (int i = 0; i < 5; ++i); t += 1;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: for loop has empty body [-Wempty-body]

Now the same mistake with the semicolon at the end of its own line:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    for (int i = 0; i < 5; ++i);
    t += 1;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ c++ -std=c++17 -Wall -Wextra -Wpedantic stray.cpp -o stray
$                                            ← zero warnings

Identical meaning, different diagnostic. Both loops have an empty body and both run t += 1 exactly once. The only difference is which line the semicolon sits on.

That is deliberate, and the note attached to the warned version says so: put the semicolon on a separate line to silence this warning. An empty loop body is occasionally intended, and clang treats a semicolon alone on its line as the way you write one on purpose. So the warning is not reporting "this body is empty" — it is reporting "this body is empty and you do not appear to have meant it", and the evidence it uses is your layout.

Misleading indentation. Here the compiler does well:

Azvya Education Pvt. Ltd.VLSI Mentor
indent.cpp — complete program
#include <iostream>

int main()
{
    int total = 0;
    for (int i = 0; i < 5; ++i)
        std::cout << "counting " << i << '\n';
        total += 1;              // indented, but NOT in the loop
    std::cout << "total = " << total << '\n';
    return 0;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
indent.cpp:8:9: warning: misleading indentation; statement is not part of the previous 'for' [-Wmisleading-indentation]

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
counting 0
counting 1
counting 2
counting 3
counting 4
total = 1

total is 1, not 5. The loop body was the single std::cout statement, and total += 1; ran once after the loop finished. Chapter 5.1 §2 asked for braces unconditionally; this is the second of the two bugs it promised, and it is why.

An empty if body is caught too — if (n > 0); gives warning: if statement has empty body [-Wempty-body].

9. What to Actually Do

Use break for a search that has found its answer, and continue for items that do not apply.

Prefer for over while when the stepping is regular — the advance is then in a slot continue cannot skip.

If you continue in a while loop, advance before it, unconditionally.

Use continue for guard clauses, when several conditions each disqualify an item; invert the condition instead for a single skip.

Remember break leaves one loop. For two, use a flag or move the loops into a function and return.

Remember break in a switch ends the switch, not the enclosing loop.

Count up and stop before the bound, and trace n = 0, n = 1 and the last iteration.

Write braces always. §8's indentation bug and Chapter 5.1 §7's dangling else both disappear.

Add -Wimplicit-fallthrough and -Wtautological-unsigned-zero-compare to your build.

10. Mistakes Worth Making Once

continue in a while loop before the advance. Measured: stuck at i = 2 forever, with zero warnings.

Assuming continue behaves the same in for and while. Measured: the for version printed 0, 1, 3, 4 and terminated.

Expecting continue in a do-while to restart the body. It jumps to the condition at the bottom.

Expecting break to leave two loops. Measured: r0c0 r1c0 r2c0 — the outer loop ran to completion.

Confusing a switch's break with a loop's. The inner construct wins.

A stray semicolon after a loop header. Measured: warned on one line, silent on two.

Trusting indentation. Measured: total = 1, not 5 — though here the compiler does warn.

Assuming -Wall -Wextra is enough. Four of this module's measured bugs are outside it.

11. Interview Relevance

"What is the difference between break and continue?" is a warm-up: one leaves the loop, one skips to the next iteration. The follow-up is the real question.

"Is continue ever dangerous?" Yes, and the answer is specific: in a while or do-while loop, continue skips the remainder of the body including any statement that was advancing the loop, so a loop that looks correct can run forever. In a for loop it cannot, because the advance is in the header. Saying which loop forms are affected and why is what separates a memorised answer from an understood one.

"How do you exit two nested loops?" There is no break 2;. Name both options — a flag the outer condition tests, or extracting the loops into a function and using return — and prefer the second.

12. What You Should Be Able to Say Now

  1. break leaves the loop; continue abandons the current iteration. Both act on the innermost enclosing loop.
  2. Measured: break in a nested loop gave r0c0 r1c0 r2c0 — the outer loop ran to completion. There is no break 2;.
  3. Inside a switch, break ends the switch, not an enclosing loop.
  4. In a for loop, continue still runs the advance. Measured: 0, 1, 3, 4, and it terminated.
  5. In a while loop, continue skips the advance. Measured: stuck at i = 2 forever, zero warnings.
  6. That loop is well-defined, not Chapter 5.4's undefined kind, because its body prints — it simply never finishes.
  7. Three fixes, in order: use a for, advance before the continue, or invert the condition.
  8. In a do-while, continue jumps to the condition at the bottom — measured: body entered 5 times, tail reached 3.
  9. Off-by-one: i <= n runs n + 1 times. Fix it with the half-open convention, and check n = 0, n = 1 and the last iteration.
  10. The never-ending loop has three causes: no progress statement (5.4, and undefined), a condition that can never be false (5.5's unsigned index), and a progress statement that gets skipped (§4).
  11. Measured, and the finding worth keeping: a stray semicolon after a loop header warns on one line and is silent on two — the diagnostic depends on layout, because an empty body is sometimes intended.
  12. Measured: misleading indentation is warned, and total was 1 rather than 5.
  13. The module's uncaught bugs are exactly those whose deliberate use is spelled identically — fallthrough, unbounded loops, unsigned indices, by-value loop variables.
  14. So: enable -Wimplicit-fallthrough and -Wtautological-unsigned-zero-compare, and for the rest the discipline is the tool.

13. Exercises

Exercise 1 — trace the skip. Give the output of §3's for loop and of §4's while loop without its guard, and name the one line that accounts for the difference.

Exercise 2 — fix it three ways. Repair §4's loop so it prints i = 0, 1, 3, 4 and terminates, using each of the three remedies. Say which you would ship and why.

Exercise 3 — where does continue go? For a for, a while and a do-while, say exactly what runs next after a continue, and which of the three can therefore skip a progress statement.

Exercise 4 — leave both loops. Write a nested loop that stops entirely on the first match, two ways: with a flag, and by extracting a function. Say which reads better.

Exercise 5 — which break? In a loop containing a switch containing a break, say what that break ends, and write the code that leaves the loop from inside the switch.

Exercise 6 — predict the diagnostic. For each, say whether -Wall -Wextra -Wpedantic warns, and give the text where it does: (a) for (int i = 0; i < 5; ++i); t += 1; on one line (b) the same across two lines (c) if (n > 0); (d) a loop body indented but unbraced, with a second statement (e) a switch case missing its break.

Exercise 7 — name the cause. For each never-ending loop, say which of §7's three causes it is and give the fix: (a) while (busy) { } (b) for (unsigned i = n; i >= 0; --i) (c) while (i < n) { if (skip(i)) continue; ++i; } (d) while (n != 1) { n -= 2; } with n starting at 10.

14. Summary

  • break leaves the loop; continue skips the iteration; both act on the innermost loop.
  • Measured: break in nested loops gave r0c0 r1c0 r2c0. There is no break 2;.
  • In a switch, break ends the switch.
  • continue in a for still runs the advance (measured: 0, 1, 3, 4) and in a while it does not (measured: stuck forever, zero warnings).
  • That stuck loop is well-defined — it prints, so it is observable — and it simply never ends.
  • In a do-while, continue jumps down to the condition — body entered 5 times, tail reached 3.
  • Off-by-one: i <= n runs n + 1 times; the half-open convention removes the arithmetic.
  • Three causes of a never-ending loop: no advance, a condition that cannot be false, an advance that is skipped.
  • Measured: a stray semicolon warns on one line and is silent on two; misleading indentation is warned, and total was 1.
  • The bugs -Wall -Wextra misses are exactly those whose deliberate use is spelled the same way — so enable the extra flags and let discipline cover the rest.

Module 5 is finished. With Module 3's types and Module 4's expressions, you can now write a complete program: it can decide, it can repeat, and you can say why each of its loops terminates. That is enough to solve a real problem, and it is also the point at which a program written as one long main stops being workable — because every name is visible to every line, every piece of logic is reachable from every other, and nothing can be tested on its own.

Module 6 is the answer, and its first job is smaller than it sounds. A function is not primarily a way to avoid repeating yourself; it is a way to make a contract — this goes in, that comes out — so that a reader can use it without reading it. The module establishes one detail completely rather than quickly, because almost everything in Modules 8, 12 and 13 depends on the reader having it exactly right: when you call a function, the argument is copied into the parameter. What that means for an int is obvious. What it means for everything else is most of the rest of this curriculum.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.