Loops: Iterative Control Flow in C++

Introduction

Loops are a fundamental construct in programming that allow repetitive execution of a block of code. In C++, a versatile and powerful programming language, loops come in various forms, each serving distinct purposed. In this chapter, we will delve into the world of loops in C++, exploring their types, syntax, use cased, and best practices to empower you with the knowledge needed to harness their full potential.

Types of Loops

1️⃣ for Loop:

The for loop is widely used for iterating a specific number of times. Its syntax includes initialization, condition, and iteration expressions.

Syntax:
for (initialization; condition; iteration) {
    // Code to be repeated
}
Example:
#include <iostream>
using namespace std;

int main() {
    // Print numbers from 1 to 5
    for (int i = 1; i <= 5; i++) {
        cout << i << " ";
    }
    return 0;
}
  • Initialization: The loop starts by initializing i to 1.
  • Condition: The loop runs as long as i <= 5.
  • Increment: After each iteration, i is incremented by 1.
Output:
1 2 3 4 5

2️⃣ while Loop:

The while loop continues execution as long as a given condition is true. It is often used when the number of iterations is unknown before entering the loop. If the condition is false at the beginning, the loop will not run at all.

Syntax:
while (condition) {
    // Code to be repeated
}
Example:
#include <iostream>
using namespace std;

int main() {
    int i = 1;
    // Print numbers from 1 to 5
    while (i <= 5) {
        cout << i << " ";
        i++;  // Increment i
    }
    return 0;
}
  • The loop will continue executing as long as i <= 5 is true.
  • After each iteration, i is incremented by 1.
Output:
1 2 3 4 5

3️⃣ do-while Loop:

The do-while loop is similar to the while loop but guarantees at least one time execution of the loop body regardless of the condition.

Syntax:
do {
    // Code to be repeated
} while (condition);
Example:
#include <iostream>
using namespace std;

int main() {
    int i = 1;
    // Print numbers from 1 to 5
    do {
        cout << i << " ";
        i++;
    } while (i <= 5);
    return 0;
}
  • The do block is executed first.
  • After each execution, the condition i <= 5 is checked.
  • This loop is useful when you want to ensure that the code block runs at least once, even if the condition is false.
Output:
1 2 3 4 5

4️⃣ Range based Loop (C++):

The range-based for loop, introduced in C++11, provides a simpler and more concise way to iterate over containers, such as arrays, vectors, and other collections. It eliminates the need for explicit iterators or indexing, making your code cleaner and less error-prone.

Syntax:

The syntax for a range-based for loop is as follows:

for (declaration : container) {
    // Code to be executed for each element
}
  • declaration: This specifies the variable that will hold each element of the container during each iteration.
  • container: This can be any collection type that supports iteration (like arrays, vectors, sets, etc.).
Example:

Here’s a simple example of a range-based for loop iterating over a std::vector:

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};

    // Range-based for loop
    for (int num : numbers) {
        cout << num << " ";
    }
    return 0;
}
Output:
1 2 3 4 5

Modifying Elements:

If you want to modify the elements of a container while iterating, you can use a reference in the loop declaration:

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};

    // Modifying elements
    for (int& num : numbers) {
        num *= 2;  // Double each number
    }

    // Print modified numbers
    for (int num : numbers) {
        cout << num << " ";
    }
    return 0;
}
Output:
2 4 6 8 10

Iterating Over Arrays:

Range-based for loops can also be used with C-style arrays:

#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    // Range-based for loop for array
    for (int num : arr) {
        cout << num << " ";
    }
    return 0;
}
Output:
10 20 30 40 50

Loop Control Statements:

1️⃣ break Statement:

  • Exits the loop prematurely based on a specified condition.

The break statement immediately terminates the loop, and the control is passed to the code following the loop.

Example:
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break;  // Exit the loop when i equals 3
    }
    cout << i << " ";
}
Output:
1 2

2️⃣ continue Statement:

  • Skips the rest of the loop body and moves to the next iteration based on a specified condition.

The continue statement skips the rest of the code in the current iteration and jumps to the next iteration of the loop.

Example:
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue;  // Skip the iteration when i equals 3
    }
    cout << i << " ";
}
Output:
1 2 4 5

Choosing the Right Loop:

for Loop:

  • Suitable for a known number of iterations.
  • Elegant for iterating over a range of collection.

while Loop:

  • Appropriate when the number of iterations is not known in advance.
  • Useful for infinite loops controlled by external conditions.

do-while Loop:

  • Ensures at least one execution of the loop body.
  • Ideal for situations where the loop body must be executed before checking the condition.

Infinite Loops

An infinite loop is a loop that runs indefinitely because the condition never becomes false. Be careful when writing loops to avoid infinite loops unless intended.

Examples of Infinite Loops:

1️⃣ Infinite for Loop:

for (;;) {
    cout << "This will run forever!";
}

2️⃣ Infinite while Loop:

while (true) {
    cout << "This will run forever!";
}

3️⃣ Infinite do-while Loop:

do {
    cout << "This will run forever!";
} while (true);

Common Mistakes in Loops

Loops are essential for iterating over data and performing repetitive tasks, but they can also lead to bugs if not handled correctly.

1️⃣ Off-by-One Errors:

This is one of the most common mistakes. It occurs when the loop iterates one time too many or too few.

Example:

for (int i = 0; i <= 5; i++) {  // Off-by-one: Should be i < 5
    cout << i << " ";  // Outputs 0 to 5 instead of 0 to 4
}

2️⃣ Infinite Loops:

An infinite loop occurs when the loop condition never becomes false. This can happen if you forget to update the loop variable or if the condition is incorrect.

Example:

int i = 0;
while (i < 5) {
    cout << i << " ";
    // Missing increment: i++;  // This will cause an infinite loop
}

3️⃣ Incorrect Initialization:

Failing to initialize the loop control variable correctly can lead to unintended behavior or infinite loops.

Example:

for (int i = 1; i <= 10; i++) {  // Correct initialization
    cout << i << " ";
}

for (int i; i <= 10; i++) {  // Undefined behavior: i is uninitialized
    cout << i << " ";
}

4️⃣ Not Using the Correct Data Type:

Using an incorrect data type for the loop variable can lead to logic errors, especially with integer overflows.

Example:

for (unsigned int i = 0; i >= 0; i++) {  // Will never end because i can't be negative
    cout << i << " ";
}

5️⃣ Modifying Loop Control Variables Inside the Loop:

Changing the loop control variable within the loop can lead to unexpected behavior and make the loop harder to understand.

Example:

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        i++;  // Skipping an iteration
    }
    cout << i << " ";
}

6️⃣ Using Incorrect Loop Condition:

Writing a condition that doesn't reflect the intended logic can lead to premature exit or unnecessary iterations.

Example:

for (int i = 0; i < 10; i++) {  // Intended to loop 10 times
    if (i == 5) {
        i = 10;  // Forces the loop to terminate early
    }
    cout << i << " ";
}

7️⃣ Nested Loop Confusion:

When using nested loops, it's easy to confuse the inner and outer loop control variables, which can lead to logic errors.

Example:

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        cout << i << " " << j << endl;
        i++;  // Modifying the outer loop variable can cause confusion
    }
}

8️⃣ Not Handling Container Size Changes:

If you modify the size of a container inside a loop (e.g., removing elements from a vector), it can lead to accessing invalid indices.

Example:

vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); i++) {
    if (vec[i] % 2 == 0) {
        vec.erase(vec.begin() + i);  // Invalidates the loop
    }
}

9️⃣ Neglecting Loop Efficiency:

Inefficient loops can lead to performance issues, especially when nesting multiple loops or performing unnecessary computations.

Example:

for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
        // Doing something expensive
    }
}  // This O(n^2) complexity can become an issue for large n