Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Controlling Loop Execution
Exit loops early with break or skip iterations with continue.
Exiting loops and iterations early
Loops normally run until their condition becomes false. But sometimes you need more control - exiting a loop entirely when a goal is reached, or skipping specific iterations while continuing the loop. C++ provides two statements for this: break and continue.
The break statement
The break statement immediately terminates the enclosing loop or switch, transferring execution to the first statement after that construct.
You've already seen break in switch statements, where it prevents fallthrough between cases. But break works with any loop type: while, do-while, and for.
Exiting a loop early
Consider a program that searches for a specific value in user input:
#include <iostream>
int main()
{
const int targetValue{ 42 };
bool found{ false };
std::cout << "I'm thinking of a number. Try to guess it!\n";
for (int attempts{ 1 }; attempts <= 5; ++attempts)
{
std::cout << "Attempt " << attempts << ": ";
int guess{};
std::cin >> guess;
if (guess == targetValue)
{
std::cout << "Correct! You found it!\n";
found = true;
break; // no need to continue guessing
}
std::cout << "Not quite. Try again.\n";
}
if (!found)
std::cout << "Out of attempts. The number was " << targetValue << ".\n";
return 0;
}
Sample run:
I'm thinking of a number. Try to guess it!
Attempt 1: 10
Not quite. Try again.
Attempt 2: 42
Correct! You found it!
Without break, the loop would continue asking for guesses even after finding the correct answer. The break statement exits immediately once the goal is achieved.
Infinite loops with exit conditions
Sometimes you don't know in advance how many iterations you'll need. An infinite loop with a break condition handles this elegantly:
#include <iostream>
int main()
{
std::cout << "Enter words (type 'quit' to stop):\n";
int wordCount{ 0 };
while (true) // runs until break
{
std::string word{};
std::cin >> word;
if (word == "quit")
break;
++wordCount;
}
std::cout << "You entered " << wordCount << " words.\n";
return 0;
}
Sample run:
Enter words (type 'quit' to stop):
hello world this is a test quit
You entered 6 words.
The loop has no natural termination condition in its header - it relies entirely on break to exit. This pattern is common when the exit condition depends on user input or runtime state.
Break vs return
New programmers sometimes confuse break and return. They're fundamentally different:
- break exits only the loop (or switch), then continues with code after it
- return exits the entire function, returning to the caller
#include <iostream>
void searchForValue(int target)
{
for (int i{ 1 }; i <= 100; ++i)
{
if (i == target)
{
std::cout << "Found " << target << " at position " << i << '\n';
break; // exit loop, continue in function
}
}
// break causes execution to continue here
std::cout << "Search complete.\n";
}
int findValue(int target)
{
for (int i{ 1 }; i <= 100; ++i)
{
if (i == target)
{
return i; // exit function immediately
}
}
// return skips this entirely
std::cout << "This never prints if target is found.\n";
return -1;
}
int main()
{
std::cout << "Using break:\n";
searchForValue(50);
std::cout << "\nUsing return:\n";
int result{ findValue(50) };
std::cout << "Returned: " << result << '\n';
return 0;
}
Output:
Using break:
Found 50 at position 50
Search complete.
Using return:
Returned: 50
Notice that searchForValue prints "Search complete" after the loop because break only exits the loop. In findValue, the final std::cout never executes when the target is found because return exits the entire function.
The continue statement
While break exits a loop entirely, the continue statement skips the rest of the current iteration and jumps to the next one. The loop keeps running - only the current iteration is cut short.
#include <iostream>
int main()
{
std::cout << "Odd numbers from 1 to 20:\n";
for (int i{ 1 }; i <= 20; ++i)
{
if ((i % 2) == 0) // if even
continue; // skip to next iteration
std::cout << i << ' ';
}
std::cout << '\n';
return 0;
}
Output:
Odd numbers from 1 to 20:
1 3 5 7 9 11 13 15 17 19
When continue executes, control jumps to the end of the loop body. For a for loop, the end-expression (like ++i) still runs before the next iteration begins.
Continue works differently in while loops
In a for loop, the increment happens after each iteration regardless of how the iteration ended. But in a while loop, if the increment is inside the loop body, continue can skip it - potentially creating an infinite loop:
#include <iostream>
int main()
{
int i{ 0 };
while (i < 10)
{
if (i == 5)
continue; // WARNING: skips the increment!
std::cout << i << ' ';
++i; // this line is skipped when i == 5
}
return 0;
}
This prints 0 1 2 3 4 and then loops forever. When i reaches 5, continue skips the ++i, so i stays 5 indefinitely.
The fix is to increment before the continue:
#include <iostream>
int main()
{
int i{ 0 };
while (i < 10)
{
++i; // increment first
if (i == 5)
continue;
std::cout << i << ' ';
}
return 0;
}
Output:
1 2 3 4 6 7 8 9 10
This is one reason to prefer for loops when you have a counter variable - the increment is guaranteed to run.
Using break and continue effectively
Some programmers advise against break and continue because they make control flow less linear. However, when used well, they actually improve readability by reducing nesting and eliminating extra variables.
Reducing nesting with continue
Compare these two approaches to processing only valid data:
// Without continue - deeply nested
for (int i{ 0 }; i < dataSize; ++i)
{
if (isValid(data[i]))
{
if (meetsThreshold(data[i]))
{
if (!alreadyProcessed(data[i]))
{
processData(data[i]);
}
}
}
}
// With continue - flat structure
for (int i{ 0 }; i < dataSize; ++i)
{
if (!isValid(data[i]))
continue;
if (!meetsThreshold(data[i]))
continue;
if (alreadyProcessed(data[i]))
continue;
processData(data[i]);
}
The second version reads more naturally: skip invalid data, skip data below threshold, skip already-processed data, then process what remains. Each continue acts as a guard clause, filtering out cases we don't want.
Eliminating flag variables with break
Compare these approaches to finding an element:
// Without break - needs a flag variable
bool found{ false };
int index{ -1 };
for (int i{ 0 }; i < size && !found; ++i)
{
if (items[i] == target)
{
found = true;
index = i;
}
}
// With break - clearer intent
int index{ -1 };
for (int i{ 0 }; i < size; ++i)
{
if (items[i] == target)
{
index = i;
break;
}
}
The second version eliminates the found variable and the complex loop condition. The intent - find the item and stop looking - is immediately clear.
Use break and continue when they simplify loop logic by reducing nesting or eliminating unnecessary variables.
Early returns
A return statement that appears before the end of a function is called an early return. The same debate applies: some developers prefer a single return at the end for predictability, while others use early returns to exit as soon as the function's work is done.
Early returns are particularly useful for validation at the start of a function:
int processOrder(int quantity, double pricePerUnit)
{
if (quantity <= 0)
return -1; // invalid quantity
if (pricePerUnit <= 0.0)
return -2; // invalid price
// main logic only runs if inputs are valid
double total{ quantity * pricePerUnit };
applyDiscount(total);
generateInvoice(total);
return 0; // success
}
Without early returns, the main logic would need to be wrapped in nested conditionals checking that quantity and price are valid.
Use early returns when they simplify function logic, particularly for input validation.
Summary
- break immediately exits the enclosing loop or switch and continues with the next statement after it
- continue skips the rest of the current iteration and begins the next iteration
- break vs return: break exits only the loop; return exits the entire function
- In for loops, the end-expression runs after continue (the increment still happens)
- In while/do-while loops, continue skips everything after it in the body, including any increment statements - this can cause infinite loops if you're not careful
- Use break to exit a loop early when a goal is reached or an error is detected
- Use continue to skip unwanted iterations, particularly as guard clauses at the start of loop bodies
- Both reduce nesting and eliminate flag variables when used appropriately
- Early returns serve a similar purpose in functions, allowing immediate exit when the function's work is complete or inputs are invalid
Controlling Loop Execution - Quiz
Test your understanding of the lesson.
Practice Exercises
Skip and Stop
Use break and continue to control loop execution - skip certain iterations and exit early.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!