Control Flow: Conditionals and Loops
Hey there! Welcome back. Today, we’re diving into the brains of our program: Control Flow.
Normally, C++ executes code line by line, from top to bottom. But what if you want to make decisions? What if you want to run a block of code only if a user is logged in, or repeat a print statement 100 times without copy-pasting it? That’s where control flow comes to the rescue. It’s essentially the traffic cop of your codebase, guiding execution exactly where it needs to go.
Types of Control Flow Statements
In C++, control flow statements fall into three main categories:
- Conditional Statements: For making decisions (e.g., if/else, switch-case).
- Iteration Statements (Loops): For repeating tasks (e.g., for, while, do-while).
- Jump Statements: For breaking out of loops or returning early (e.g., break, continue, return).
Here’s a bird’s-eye view of how they fit together:
flowchart TD
CF[Control Flow Statements] --> Cond[Conditional Statements]
CF --> Iter[Iteration Statements / Loops]
CF --> Jump[Jump Statements]
Cond --> IfElse[if / else]
Cond --> Switch[switch]
Cond --> Ternary[Ternary Operator]
Iter --> For[for Loop]
Iter --> While[while Loop]
Iter --> DoWhile[do-while Loop]
Jump --> Break[break]
Jump --> Continue[continue]
Jump --> Return[return]
Let’s break these down one by one.
Conditional Statements (Selection Statements)
Conditional statements allow your program to evaluate a condition (which results in true or false) and take different paths depending on the outcome.
The if/else Statement
This is the classic decision-maker. The if block executes only if its condition evaluates to true. If it’s false, the optional else block runs instead.
Syntax:
if (condition) {
// runs if condition is true
} else {
// runs if condition is false
}
Let’s look at a quick example checking a test score:
Example:
#include <iostream>
int main() {
int score = 85;
if (score >= 50) {
std::cout << "Nice job! You passed the test." << std::endl;
} else {
std::cout << "Oof, that's a fail. Time to hit the books!" << std::endl;
}
return 0;
}
Output:
Nice job! You passed the test.
The if-else-if Ladder
When you have more than just two choices, you can chain multiple conditions together using else if. C++ will evaluate them sequentially from top to bottom and execute the first block that returns true.
Syntax:
if (condition1) {
// runs if condition1 is true
} else if (condition2) {
// runs if condition1 is false AND condition2 is true
} else {
// runs if all above conditions are false
}
Example:
#include <iostream>
int main() {
int score = 85;
if (score >= 90) {
std::cout << "Grade: A (Absolute Legend)" << std::endl;
} else if (score >= 75) {
std::cout << "Grade: B (Solid effort!)" << std::endl;
} else if (score >= 50) {
std::cout << "Grade: C (Passed)" << std::endl;
} else {
std::cout << "Grade: F (Better luck next time)" << std::endl;
}
return 0;
}
Output:
Grade: B (Solid effort!)
Nested if-else
Sometimes, you need to check secondary conditions inside an existing decision. You can nest if-else statements inside \{ \} blocks of other if-else statements as deep as you need.
Example:
#include <iostream>
int main() {
int age = 20;
bool hasLicense = true;
if (age >= 18) {
if (hasLicense) {
std::cout << "You're good to drive! Hit the highway." << std::endl;
} else {
std::cout << "You're old enough, but you need a license first!" << std::endl;
}
} else {
std::cout << "Too young to drive, buddy." << std::endl;
}
return 0;
}
Output:
You're good to drive! Hit the highway.
The Ternary Operator
If you have a super simple if-else that just assigns a value or returns something, writing out 6 lines of code can feel like overkill. C++ gives us a shorthand called the Ternary Operator (? :).
Syntax:
condition ? expression_if_true : expression_if_false;
Here is how you can use it to simplify a pass/fail check:
Example:
#include <iostream>
#include <string>
int main() {
int score = 85;
std::string result = (score >= 50) ? "Pass" : "Fail";
std::cout << "Result: " << result << std::endl;
return 0;
}
Output:
Result: Pass
The switch Statement
If you’re writing an if-else-if ladder with 10 different options checking the exact same variable (like matching a menu option or weekday), your code gets messy fast. The switch statement makes this look clean.
It matches the value of an expression to specific labels called case. If a match is found, the code under that case runs.
[!WARNING] Always remember to put a
breakstatement at the end of each case! If you forget, C++ will execute all subsequent cases regardless of whether they match. This is called fallthrough.
Syntax:
switch (expression) {
case value1:
// runs if expression == value1
break;
case value2:
// runs if expression == value2
break;
default:
// runs if no cases match
}
Example:
#include <iostream>
int main() {
int day = 3;
switch (day) {
case 1:
std::cout << "Monday" << std::endl;
break;
case 2:
std::cout << "Tuesday" << std::endl;
break;
case 3:
std::cout << "Wednesday" << std::endl;
break;
case 4:
std::cout << "Thursday" << std::endl;
break;
case 5:
std::cout << "Friday" << std::endl;
break;
default:
std::cout << "Weekend vibes!" << std::endl;
}
return 0;
}
Output:
Wednesday
Iteration Statements (Loops)
Loops are used when you want to execute a block of code repeatedly. C++ offers three classic flavors: for, while, and do-while.
The for Loop
Use a for loop when you know exactly how many times you want to run a task (e.g., printing a list of 10 items, or running from 1 to 100). It groups three control statements: initialization, check condition, and updation.
Flowchart:
flowchart TD
Start([Start]) --> Init[Initialization: statement 1]
Init --> Check{Check Condition: statement 2}
Check -- True --> LoopBody[Execute Loop Code Block]
LoopBody --> Update[Updation: statement 3]
Update --> Check
Check -- False --> End([Stop])
Syntax:
for (initialization; condition; updation) {
// code inside loop to be executed
}
- Initialization (statement 1): Sets up your counter variable (runs once at the very start).
- Condition (statement 2): Checked before every iteration. If it’s true, the loop body runs. If false, the loop ends.
- Updation (statement 3): Runs at the end of every loop iteration (e.g., increments the counter).
Let’s print numbers 1 through 5:
Example:
#include <iostream>
int main() {
for (int i = 1; i <= 5; ++i) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Output:
1 2 3 4 5
The while Loop
Use a while loop when you want to repeat a task until a condition is met, but you don’t know ahead of time how many loops that will take (e.g., waiting for user input or reading a file).
Flowchart:
flowchart TD
Start([Start]) --> Check{Check Condition}
Check -- True --> LoopBody[Execute Loop Code Block & Update]
LoopBody --> Check
Check -- False --> End([Stop])
Syntax:
while (condition) {
// code inside loop
// (don't forget to update your variables so the condition eventually becomes false!)
}
Let’s print numbers 1 through 5 using a while loop:
Example:
#include <iostream>
int main() {
int count = 1;
while (count <= 5) {
std::cout << count << " ";
count++; // increments count so we don't loop forever!
}
std::cout << std::endl;
return 0;
}
Output:
1 2 3 4 5
[!CAUTION] Infinite Loop Warning: If your condition never becomes
false, your loop will run forever, freezing your computer or consuming 100% CPU. Keep a close eye on your loop variables!
Infinite Loop Example:
#include <iostream>
int main() {
int i = 1;
// WARNING: i never changes, so i <= 5 is always true.
while (i <= 5) {
std::cout << "Oh no, infinite loop!" << std::endl;
}
return 0;
}
The do-while Loop
The do-while loop is very similar to the while loop, but with one massive difference: it evaluates the condition after running the code block. This guarantees your loop block executes at least once, even if the condition is false from the absolute start.
Flowchart:
flowchart TD
Start([Start]) --> LoopBody[Execute Loop Code Block & Update]
LoopBody --> Check{Check Condition}
Check -- True --> LoopBody
Check -- False --> End([Stop])
Syntax:
do {
// code inside loop to be executed
} while (condition);
Let’s look at a basic counter example:
Example:
#include <iostream>
int main() {
int count = 1;
do {
std::cout << count << " ";
count++;
} while (count <= 5);
std::cout << std::endl;
return 0;
}
Output:
1 2 3 4 5
Jump Statements
Jump statements immediately divert the compiler’s execution path. The most popular ones are break, continue, and return.
The break Statement
This statement immediately halts the loop or switch block it’s inside, dropping control to the next line of code outside the block.
Example:
#include <iostream>
int main() {
for (int i = 1; i <= 10; ++i) {
if (i == 5) {
break; // Exits the loop entirely when i hits 5
}
std::cout << i << " ";
}
std::cout << "\nLoop finished!" << std::endl;
return 0;
}
Output:
1 2 3 4
Loop finished!
The continue Statement
Unlike break which quits the loop, continue just skips the rest of the code in the current iteration and jumps straight to the next cycle (re-evaluating the condition or performing the updation).
Example:
#include <iostream>
int main() {
for (int i = 1; i <= 5; ++i) {
if (i == 3) {
continue; // Skip printing 3, go straight to i = 4
}
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Output:
1 2 4 5
The return Statement
The return statement exits the current function immediately and hands back a value (if the function is not void) to the caller.
Example:
#include <iostream>
void checkEven(int num) {
if (num % 2 == 0) {
std::cout << num << " is even." << std::endl;
return; // Exits the function early, skipping the odd print
}
std::cout << num << " is odd." << std::endl;
}
int main() {
checkEven(4);
checkEven(7);
return 0;
}
Output:
4 is even.
7 is odd.
Nested Loops
You can put loops inside other loops. The inner loop executes completely for every single iteration of the outer loop.
Example:
#include <iostream>
int main() {
// Outer loop runs 2 times
for (int i = 1; i <= 2; ++i) {
// Inner loop runs 3 times per outer iteration
for (int j = 1; j <= 3; ++j) {
std::cout << "i: " << i << ", j: " << j << std::endl;
}
}
return 0;
}
Output:
i: 1, j: 1
i: 1, j: 2
i: 1, j: 3
i: 2, j: 1
i: 2, j: 2
i: 2, j: 3
Application: Printing a Grid (Matrix)
A classic real-world use case for nested loops is dealing with 2D structures, like rendering a pixel grid, printing matrices, or drawing game boards. Here, the outer loop represents the rows, and the inner loop represents the columns.
Example:
#include <iostream>
int main() {
int rows = 3;
int cols = 4;
// Outer loop walks down the rows
for (int i = 1; i <= rows; ++i) {
// Inner loop prints stars across each column in the row
for (int j = 1; j <= cols; ++j) {
std::cout << "* ";
}
std::cout << std::endl; // Move to the next row
}
return 0;
}
Output:
* * * *
* * * *
* * * *
Level Up: Extra Practice & Interview Questions
Q1. What happens if you omit the break statement in a switch case?
If you skip break, the switch statement will experience fallthrough. This means C++ will execute the matching case, and then continue executing all subsequent cases sequentially, ignoring their match conditions until it hits a break or reaches the end of the switch block.
Code:
#include <iostream>
int main() {
int num = 2;
switch (num) {
case 1:
std::cout << "One" << std::endl;
case 2:
std::cout << "Two" << std::endl; // Match!
case 3:
std::cout << "Three" << std::endl; // Fallthrough!
default:
std::cout << "Default Case" << std::endl; // Fallthrough!
}
return 0;
}
Output:
Two
Three
Default Case
Q2. Analyze the following for loop and predict its output:
#include <iostream>
int main() {
for (int i = 0; i < 10; i += 2) {
if (i == 6) {
break;
}
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Answer:
The loop increments i by 2 on each run (i += 2).
- Iteration 1:
i = 0, checksi == 6(false), prints0. - Iteration 2:
i = 2, checksi == 6(false), prints2. - Iteration 3:
i = 4, checksi == 6(false), prints4. - Iteration 4:
i = 6, checksi == 6(true), breaks immediately. Because thebreakhappens before printing, the loop terminates early. - Output:
0 2 4
Q3. Write a nested loop to display the multiplication tables of 2 and 3.
Here’s how you can use nested loops to generate multiplication tables side-by-side:
Code:
#include <iostream>
int main() {
// Outer loop sets the table base (2 and 3)
for (int i = 2; i <= 3; ++i) {
std::cout << "Table of " << i << ":" << std::endl;
// Inner loop multiplies from 1 to 10
for (int j = 1; j <= 10; ++j) {
std::cout << i << " x " << j << " = " << (i * j) << std::endl;
}
std::cout << std::endl; // Blank line between tables
}
return 0;
}
Output:
Table of 2:
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
2 x 5 = 10
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
2 x 10 = 20
Table of 3:
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
3 x 4 = 12
3 x 5 = 15
3 x 6 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
3 x 10 = 30
quiz Test Your Understanding
What happens if a break statement is omitted in a switch-case block?
Without a break statement, the execution falls through to execute subsequent case blocks, even if their conditions don't match.