C++ Control Flow: if, for, while, switch
Master C++ control flow with if-else, for and range-for loops, while, do-while, switch, and the modern init-statement syntax for conditions.
What you'll learn
- ✓Use if-else and the C++17 init-statement form
- ✓Choose between for, range-for, while, and do-while
- ✓Write safe switch statements with fallthrough markers
- ✓Apply break and continue without making code unreadable
- ✓Reach for the right loop for each problem shape
Prerequisites
- •C++ basics — see /blog/cpp-variables-and-types
Control flow is how programs make decisions and repeat work. C++ inherits the C family syntax and adds a few modern conveniences that make code shorter and safer.
if and else
The condition must be a bool or something convertible to one. C++ has no truthy strings or lists; only numbers and pointers convert to bool.
int score = 82;
if (score >= 90) std::cout << "A\n";
else if (score >= 80) std::cout << "B\n";
else std::cout << "C\n";
Always brace multi-statement branches. Skipping braces on single-line if invites the classic “added a second line and broke the logic” bug.
Init-statement if
C++17 lets you declare a variable inside the condition. The variable is scoped to the if/else block only, keeping helper values from leaking.
if (auto it = cache.find(key); it != cache.end()) {
use(it->second);
} else {
miss(key);
}
Use this whenever the value is only needed for the decision. It removes a temporary from the surrounding scope.
for loops
The classic for loop has three parts: init, condition, step.
for (int i = 0; i < 10; ++i) {
std::cout << i << ' ';
}
Prefer ++i over i++ out of habit; for iterators it can be cheaper, and for int it costs nothing.
Range-for
For containers, range-for is almost always clearer.
#include <vector>
std::vector<int> nums{1, 2, 3, 4};
for (int n : nums) std::cout << n; // copies
for (const int& n : nums) std::cout << n; // read-only ref
for (int& n : nums) n *= 2; // mutate in place
Use const auto& for non-trivial element types and auto for cheap-to-copy ones.
while and do-while
while checks first, do-while runs the body at least once. Reach for do-while when you genuinely need that guarantee, such as reading input until validation passes.
int n{};
do {
std::cout << "Enter a positive integer: ";
std::cin >> n;
} while (n <= 0);
break and continue
break exits the innermost loop, continue skips to the next iteration. Used sparingly they read well; overused they turn loops into a tangle.
for (int i = 0; i < 100; ++i) {
if (i % 2 == 0) continue; // skip evens
if (i > 25) break; // stop early
std::cout << i << ' ';
}
switch
switch dispatches on an integral or enum value. Each case falls through to the next unless terminated by break or return. C++17 added [[fallthrough]] to mark intentional fallthrough so reviewers and compilers stop complaining.
enum class Status { Ok, Warning, Error };
void report(Status s) {
switch (s) {
case Status::Ok:
std::cout << "ok\n";
break;
case Status::Warning:
std::cout << "warn: ";
[[fallthrough]];
case Status::Error:
std::cout << "issue detected\n";
break;
}
}
switch cannot dispatch on strings; for that, use an if-chain or a hash map of handlers.
Nested loops and labels
C++ has no labelled break. To exit nested loops cleanly, factor the inner work into a function and use return, or use a flag.
bool found = false;
for (int i = 0; i < rows && !found; ++i) {
for (int j = 0; j < cols; ++j) {
if (grid[i][j] == target) { found = true; break; }
}
}
Refactoring into a function is almost always nicer:
std::optional<std::pair<int,int>> find(const Grid& g, int target);
Short-circuit evaluation
&& and || short-circuit: the right operand is not evaluated if the result is already decided. Lean on this to guard pointer dereferences.
if (p != nullptr && p->ready()) { /* safe */ }
The ternary
The ?: operator returns a value. Use it for short value selection, not for side effects.
int abs_x = x < 0 ? -x : x;
If either branch is more than a function call, write an if. Readability wins.
Putting it together
A small example that consumes input until end-of-stream, counting words longer than three letters:
#include <iostream>
#include <string>
int main() {
int count = 0;
for (std::string word; std::cin >> word; ) {
if (word.size() <= 3) continue;
++count;
}
std::cout << count << " long words\n";
}
The for here uses no step clause — the input read is the implicit step. Compact and idiomatic.
What is next
You can now structure logic. Next, package it into reusable units in Functions and References, then learn Pointers and Memory for low-level work.
Wrap up
Prefer range-for over indexed loops when possible. Brace every branch. Use C++17 init-statements to scope temporaries tightly. And when nested loops sprawl, the answer is usually a small function, not a clever break.