Control Flow
Directing program execution with conditions, loops, and branching statements
What You Will Learn
- How to make decisions with if-else statements
- Nested and chained conditional statements
- The ternary conditional operator
- Multi-way branching with switch
- All types of loops: while, do-while, for, and range-based for
- Nested loop patterns
- Controlling loops with break and continue
- Best practices for clean control flow
Prerequisites
- Completed Chapters 01 and 02
- Understanding of variables, data types, and operators
If-Else Statements
The if statement executes code only when a condition is true.
Basic If Statement
int age = 18;
if (age >= 18) {
std::cout << "You are an adult.\n";
}
If-Else
int score = 75;
if (score >= 60) {
std::cout << "You passed!\n";
} else {
std::cout << "You failed.\n";
}
If-Else-If Chain
int score = 85;
if (score >= 90) {
std::cout << "Grade: A\n";
} else if (score >= 80) {
std::cout << "Grade: B\n";
} else if (score >= 70) {
std::cout << "Grade: C\n";
} else if (score >= 60) {
std::cout << "Grade: D\n";
} else {
std::cout << "Grade: F\n";
}
If with Initializer (C++17)
You can declare and initialize a variable within the if statement:
// The variable is scoped to the if-else block
if (int result = calculate(); result > 0) {
std::cout << "Positive: " << result << "\n";
} else {
std::cout << "Non-positive: " << result << "\n";
}
// 'result' is not accessible here
// Useful for checking map lookups
std::map<std::string, int> ages = {{"Alice", 30}};
if (auto it = ages.find("Alice"); it != ages.end()) {
std::cout << "Found: " << it->second << "\n";
}
Single-Statement If (No Braces)
// Legal but not recommended
if (x > 0)
std::cout << "Positive\n";
// This can lead to bugs:
if (x > 0)
std::cout << "Positive\n";
std::cout << "This always prints!\n"; // NOT part of the if!
// Always use braces for clarity:
if (x > 0) {
std::cout << "Positive\n";
}
Tip
Always use braces { } even for single statements. It prevents bugs and makes the code clearer.
Nested Conditions
If statements can be nested inside each other for complex logic.
int age = 25;
bool hasLicense = true;
if (age >= 18) {
if (hasLicense) {
std::cout << "You can drive.\n";
} else {
std::cout << "You need a license.\n";
}
} else {
std::cout << "You are too young to drive.\n";
}
Combining Conditions
Often, nested conditions can be combined using logical operators:
// Instead of nested if:
if (age >= 18 && hasLicense) {
std::cout << "You can drive.\n";
}
// Multiple conditions
if (age >= 18 && age <= 65 && hasLicense) {
std::cout << "Standard driver\n";
}
// Using || (OR)
if (isAdmin || isModerator) {
std::cout << "Access granted\n";
}
// Complex conditions (use parentheses for clarity)
if ((role == "admin" || role == "mod") && isActive) {
grantAccess();
}
Short-Circuit Evaluation
Logical operators evaluate left-to-right and stop early if possible:
// Safe null pointer check
if (ptr != nullptr && ptr->isValid()) {
// ptr->isValid() is only called if ptr != nullptr
}
// If first condition is false, second isn't evaluated
if (false && expensiveFunction()) {
// expensiveFunction() never runs
}
// If first condition is true, second isn't evaluated
if (true || expensiveFunction()) {
// This block runs, expensiveFunction() never runs
}
Ternary Conditional Operator
The ternary operator ?: is a compact way to write simple if-else expressions.
Basic Syntax
// condition ? value_if_true : value_if_false
int a = 10, b = 20;
int max = (a > b) ? a : b; // max = 20
std::string status = (score >= 60) ? "Pass" : "Fail";
Ternary in Output
int age = 17;
std::cout << "You are " << (age >= 18 ? "an adult" : "a minor") << "\n";
Nested Ternary (Use Sparingly)
int score = 85;
std::string grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" :
(score >= 60) ? "D" : "F";
// Prefer if-else for complex logic - nested ternary is hard to read
Ternary as Lvalue
int a = 5, b = 10;
(a > b ? a : b) = 100; // Assigns to b since b is larger
// Now b = 100
Type Requirements
// Both branches must have compatible types
auto result = condition ? 1 : 2.5; // OK: int promoted to double
auto result = condition ? "yes" : "no"; // OK: both const char*
// This doesn't work:
// auto result = condition ? 1 : "hello"; // Error: incompatible types
Switch Statement
Switch provides multi-way branching based on a single value. It's often clearer than long if-else-if chains.
Basic Switch
int day = 3;
switch (day) {
case 1:
std::cout << "Monday\n";
break;
case 2:
std::cout << "Tuesday\n";
break;
case 3:
std::cout << "Wednesday\n";
break;
case 4:
std::cout << "Thursday\n";
break;
case 5:
std::cout << "Friday\n";
break;
case 6:
case 7:
std::cout << "Weekend\n";
break;
default:
std::cout << "Invalid day\n";
break;
}
Fallthrough
Without break, execution continues to the next case (fallthrough):
int month = 2;
int days;
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = 28; // Ignoring leap years
break;
default:
days = 0;
}
[[fallthrough]] Attribute (C++17)
switch (value) {
case 1:
doSomething();
[[fallthrough]]; // Intentional fallthrough, suppresses warning
case 2:
doSomethingElse();
break;
}
Switch with Initializer (C++17)
switch (int value = getValue(); value) {
case 0:
std::cout << "Zero\n";
break;
case 1:
std::cout << "One\n";
break;
default:
std::cout << "Other: " << value << "\n";
}
Switch with Enum
enum class Color { RED, GREEN, BLUE };
Color c = Color::GREEN;
switch (c) {
case Color::RED:
std::cout << "Red\n";
break;
case Color::GREEN:
std::cout << "Green\n";
break;
case Color::BLUE:
std::cout << "Blue\n";
break;
// No default needed if all cases covered
}
Switch Limitations
- Can only switch on integral types (int, char, enum), not strings or floats
- Cases must be constant expressions
- Cannot declare variables directly in cases without braces
// Variables in cases need their own scope
switch (x) {
case 1: {
int temp = 10; // OK: in its own block
std::cout << temp;
break;
}
case 2:
// int temp = 20; // Error without braces!
break;
}
While Loop
The while loop executes as long as a condition is true. The condition is checked before each iteration.
Basic While Loop
int count = 0;
while (count < 5) {
std::cout << count << " ";
count++;
}
// Output: 0 1 2 3 4
While with User Input
std::string input;
std::cout << "Enter 'quit' to exit:\n";
while (input != "quit") {
std::cout << "> ";
std::cin >> input;
std::cout << "You entered: " << input << "\n";
}
Infinite Loop
// Runs forever (or until break/return)
while (true) {
// Process events
if (shouldExit()) {
break;
}
}
While with Stream Input
int number;
int sum = 0;
std::cout << "Enter numbers (non-number to stop):\n";
while (std::cin >> number) {
sum += number;
}
std::cout << "Sum: " << sum << "\n";
Do-While Loop
The do-while loop executes at least once, then continues while the condition is true. The condition is checked after each iteration.
Basic Do-While
int count = 0;
do {
std::cout << count << " ";
count++;
} while (count < 5);
// Output: 0 1 2 3 4
Menu Example
int choice;
do {
std::cout << "\n=== Menu ===\n";
std::cout << "1. Option A\n";
std::cout << "2. Option B\n";
std::cout << "3. Exit\n";
std::cout << "Choose: ";
std::cin >> choice;
switch (choice) {
case 1:
std::cout << "You chose A\n";
break;
case 2:
std::cout << "You chose B\n";
break;
case 3:
std::cout << "Goodbye!\n";
break;
default:
std::cout << "Invalid choice\n";
}
} while (choice != 3);
Input Validation
int age;
do {
std::cout << "Enter your age (0-120): ";
std::cin >> age;
} while (age < 0 || age > 120);
std::cout << "Valid age: " << age << "\n";
For Loop
The for loop is ideal when you know the number of iterations in advance.
Basic For Loop
// for (initialization; condition; update)
for (int i = 0; i < 5; i++) {
std::cout << i << " ";
}
// Output: 0 1 2 3 4
Loop Components
// Any part can be omitted (but keep semicolons)
int i = 0;
for (; i < 5; ) {
std::cout << i << " ";
i++;
}
// Infinite loop
for (;;) {
// Runs forever
break; // Need a break to exit
}
Multiple Variables
for (int i = 0, j = 10; i < j; i++, j--) {
std::cout << "i=" << i << ", j=" << j << "\n";
}
// i=0, j=10
// i=1, j=9
// i=2, j=8
// ... until i >= j
Counting Backwards
for (int i = 10; i >= 0; i--) {
std::cout << i << " ";
}
// Output: 10 9 8 7 6 5 4 3 2 1 0
Step Size
// Count by 2s
for (int i = 0; i <= 10; i += 2) {
std::cout << i << " ";
}
// Output: 0 2 4 6 8 10
Iterating Over Arrays
int arr[] = {10, 20, 30, 40, 50};
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
Range-Based For Loop (C++11)
The range-based for loop simplifies iteration over containers and arrays.
Basic Range-Based For
int arr[] = {1, 2, 3, 4, 5};
for (int x : arr) {
std::cout << x << " ";
}
// Output: 1 2 3 4 5
With Vectors
#include <vector>
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
for (const std::string& name : names) {
std::cout << name << "\n";
}
By Value, Reference, and Const Reference
std::vector<int> nums = {1, 2, 3, 4, 5};
// By value (copy) - doesn't modify original
for (int x : nums) {
x *= 2; // Only modifies the copy
}
// By reference - modifies original
for (int& x : nums) {
x *= 2; // Doubles each element
}
// By const reference - read-only, no copy
for (const int& x : nums) {
std::cout << x << " ";
}
With auto
std::map<std::string, int> ages = {{"Alice", 30}, {"Bob", 25}};
for (const auto& pair : ages) {
std::cout << pair.first << ": " << pair.second << "\n";
}
// With structured bindings (C++17)
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << "\n";
}
Initializer List
for (int x : {1, 2, 3, 4, 5}) {
std::cout << x << " ";
}
Best Practice
Use const auto& for reading elements to avoid copies. Use auto& when you need to modify elements.
Nested Loops
Loops can be nested inside each other for multi-dimensional operations.
Multiplication Table
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
std::cout << std::setw(4) << (i * j);
}
std::cout << "\n";
}
// Output:
// 1 2 3 4 5
// 2 4 6 8 10
// 3 6 9 12 15
// 4 8 12 16 20
// 5 10 15 20 25
2D Array Traversal
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << "\n";
}
Pattern Printing
// Right triangle
int n = 5;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
std::cout << "* ";
}
std::cout << "\n";
}
// Output:
// *
// * *
// * * *
// * * * *
// * * * * *
// Pyramid
for (int i = 1; i <= n; i++) {
for (int j = n; j > i; j--) {
std::cout << " ";
}
for (int k = 1; k <= (2 * i - 1); k++) {
std::cout << "*";
}
std::cout << "\n";
}
Finding Pairs
std::vector<int> nums = {1, 2, 3, 4, 5};
int target = 6;
for (size_t i = 0; i < nums.size(); i++) {
for (size_t j = i + 1; j < nums.size(); j++) {
if (nums[i] + nums[j] == target) {
std::cout << nums[i] << " + " << nums[j] << " = " << target << "\n";
}
}
}
// Output:
// 1 + 5 = 6
// 2 + 4 = 6
Break and Continue
These statements alter the normal flow of loops.
break
break immediately exits the innermost loop or switch.
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // Exit loop when i is 5
}
std::cout << i << " ";
}
// Output: 0 1 2 3 4
continue
continue skips to the next iteration of the loop.
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
std::cout << i << " ";
}
// Output: 1 3 5 7 9
Break in Nested Loops
break only exits the innermost loop:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
break; // Only exits inner loop
}
std::cout << "(" << i << "," << j << ") ";
}
}
// Output: (0,0) (1,0) (2,0)
Breaking Out of Nested Loops
To break out of multiple loops, use a flag or put the loops in a function:
// Method 1: Flag variable
bool found = false;
for (int i = 0; i < 10 && !found; i++) {
for (int j = 0; j < 10 && !found; j++) {
if (matrix[i][j] == target) {
std::cout << "Found at (" << i << "," << j << ")\n";
found = true;
}
}
}
// Method 2: Function with return
void findTarget(int matrix[][10], int target) {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (matrix[i][j] == target) {
std::cout << "Found!\n";
return; // Exits function, thus both loops
}
}
}
}
Labeled Statements (Avoid)
C++ doesn't have labeled break like Java. Some use goto as an alternative (generally discouraged):
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (condition) {
goto done; // Jump to label
}
}
}
done:
// Continue here after breaking out
Goto Statement
The goto statement jumps to a labeled statement. It's generally discouraged because it makes code harder to follow.
// Legal but not recommended
int i = 0;
loop:
std::cout << i << " ";
i++;
if (i < 5) {
goto loop;
}
// Output: 0 1 2 3 4
Warning
Avoid goto in almost all cases. It creates "spaghetti code" that's hard to read and maintain. Use loops, functions, and structured control flow instead.
Rare Legitimate Uses
Some argue goto is acceptable for:
- Breaking out of deeply nested loops
- Cleanup code in C-style error handling
- State machines (though switch is usually better)
Common Mistakes
Assignment Instead of Comparison
int x = 5;
if (x = 10) { // Wrong! This assigns 10 to x, always true
// ...
}
// Correct: if (x == 10)
Off-By-One Errors
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { // Wrong! Should be i < 5
std::cout << arr[i]; // arr[5] is out of bounds
}
Infinite Loops
int i = 0;
while (i < 10) {
std::cout << i;
// Forgot to increment i!
}
Missing break in switch
switch (x) {
case 1:
doA();
// Missing break! Falls through to case 2
case 2:
doB();
break;
}
Dangling Else
// Which if does the else belong to?
if (a)
if (b)
doSomething();
else // Belongs to inner if, not outer!
doSomethingElse();
// Use braces to be clear:
if (a) {
if (b) {
doSomething();
}
} else {
doSomethingElse();
}
Modifying Loop Variable in Range-For
std::vector<int> nums = {1, 2, 3};
for (int n : nums) {
n *= 2; // Only modifies copy, not original!
}
// nums is still {1, 2, 3}
// Use reference to modify:
for (int& n : nums) {
n *= 2; // Now modifies original
}
Practice Questions
- Write a program that checks if a number is positive, negative, or zero.
- Create a grade calculator that converts a numeric score to a letter grade.
- Write a program that prints all even numbers from 1 to 100.
- Create a simple calculator using switch (add, subtract, multiply, divide).
- Print a multiplication table (1-10) using nested loops.
- Write a program to find the factorial of a number using a loop.
- Create a number guessing game with hints ("higher" or "lower").
- Print the Fibonacci sequence up to n terms.
- Write a program that reverses the digits of an integer.
- Create a pattern printing program (pyramid, diamond, etc.).
- Write a program to check if a number is prime.
- Use C++17 if-with-initializer to check map lookups.