Control Flow

For Loops

Master C++ for loops — traditional for, range-based for (C++11), nested loops, break, continue, and loop optimisation patterns.

For Loops

A for loop repeats a block of code a specified number of times. C++ provides two for loop forms: the traditional for loop (giving precise control over an index) and the range-based for loop (C++11, iterating over any container without index arithmetic).


Traditional For Loop

cpp
#include <iostream>
using namespace std;

int main() {
    // Syntax: for (init; condition; update)
    for (int i = 0; i < 5; i++) {
        cout << i << " ";
    }
    cout << "\n";
    // Output: 0 1 2 3 4

    // Count down
    for (int i = 5; i > 0; i--) {
        cout << i << " ";
    }
    cout << "\n";
    // Output: 5 4 3 2 1

    // Step by 2
    for (int i = 0; i <= 10; i += 2) {
        cout << i << " ";
    }
    cout << "\n";
    // Output: 0 2 4 6 8 10

    return 0;
}

The three parts of the for header are all optional:

  • Init: executed once before the loop starts
  • Condition: checked before each iteration; loop exits when false
  • Update: executed after each iteration body

Range-Based For Loop (C++11)

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

int main() {
    // Iterating over an array
    int numbers[] = {10, 20, 30, 40, 50};
    for (int n : numbers) {
        cout << n << " ";
    }
    cout << "\n";
    // Output: 10 20 30 40 50

    // Iterating over a vector
    vector<string> fruits = {"apple", "banana", "cherry"};
    for (const string& fruit : fruits) {   // const& avoids copying
        cout << fruit << "\n";
    }

    // Using auto (type deduced automatically)
    for (const auto& fruit : fruits) {
        cout << fruit.size() << " ";
    }
    cout << "\n";
    // Output: 5 6 6

    // Modifying elements (by reference)
    vector<int> v = {1, 2, 3, 4, 5};
    for (auto& elem : v) {
        elem *= 2;
    }
    // v = {2, 4, 6, 8, 10}

    return 0;
}

Note: Use const auto& when you only need to read elements — it avoids copying and works with any container type.


Nested For Loops

cpp
#include <iostream>
using namespace std;

int main() {
    // Multiplication table
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= 5; j++) {
            cout << i * j;
            if (j < 5) cout << "\t";
        }
        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

    // Triangle pattern
    for (int i = 1; i <= 5; i++) {
        for (int j = 1; j <= i; j++) {
            cout << "* ";
        }
        cout << "\n";
    }
    // *
    // * *
    // * * *
    // * * * *
    // * * * * *

    return 0;
}

break and continue

cpp
#include <iostream>
using namespace std;

int main() {
    // break — exits the loop entirely
    for (int i = 0; i < 10; i++) {
        if (i == 5) break;
        cout << i << " ";
    }
    cout << "\n";
    // Output: 0 1 2 3 4

    // continue — skips to the next iteration
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 0) continue;
        cout << i << " ";
    }
    cout << "\n";
    // Output: 1 3 5 7 9

    // break in nested loops — only breaks the innermost loop
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (j == 1) break;     // Breaks inner loop only
            cout << "(" << i << "," << j << ") ";
        }
    }
    cout << "\n";
    // Output: (0,0) (1,0) (2,0)

    return 0;
}

Breaking Out of Nested Loops

C++ does not have labelled break (unlike Java). Use a flag variable or goto:

cpp
#include <iostream>
using namespace std;

int main() {
    bool found = false;
    int targetRow = -1, targetCol = -1;
    int matrix[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    int target = 7;

    // Method 1: Flag variable
    for (int i = 0; i < 3 && !found; i++) {
        for (int j = 0; j < 4; j++) {
            if (matrix[i][j] == target) {
                targetRow = i;
                targetCol = j;
                found = true;
                break;   // Breaks inner; outer checks !found
            }
        }
    }
    if (found) cout << "Found " << target << " at [" << targetRow << "][" << targetCol << "]\n";
    // Found 7 at [1][2]

    // Method 2: goto (acceptable for breaking nested loops)
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            if (matrix[i][j] == target) {
                cout << "Found again at [" << i << "][" << j << "]\n";
                goto done;
            }
        }
    }
    done:;

    return 0;
}

Loop with Index and Element (Enumerate Pattern)

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

int main() {
    vector<string> items = {"alpha", "beta", "gamma", "delta"};

    // Traditional — when you need the index
    for (size_t i = 0; i < items.size(); i++) {
        cout << i << ": " << items[i] << "\n";
    }
    // 0: alpha
    // 1: beta
    // 2: gamma
    // 3: delta

    // Alternatively, using integer-based range
    for (int i = 0; auto& item : items) {   // C++20 init in range-for
        cout << i++ << ": " << item << "\n";
    }

    return 0;
}

Infinite Loop with for

cpp
// An infinite loop — break must exit it
for (;;) {
    cout << "Press q to quit: ";
    char ch;
    cin >> ch;
    if (ch == 'q') break;
    cout << "You pressed: " << ch << "\n";
}

Loop Optimisation Tips

cpp
#include <vector>
using namespace std;

int main() {
    vector<int> v(1000000, 1);

    // BAD: calls v.size() (and implicit conversion) every iteration
    for (int i = 0; i < v.size(); i++) { }

    // BETTER: cache size, use size_t to match container type
    for (size_t i = 0, n = v.size(); i < n; i++) { }

    // BEST for simply iterating: range-based for
    for (const auto& elem : v) { }

    // Prefer pre-increment (++i) over post-increment (i++) in loops
    // Post-increment creates a temporary copy — irrelevant for int but matters for iterators
    for (auto it = v.begin(); it != v.end(); ++it) { }

    return 0;
}

Practical Example: Sorting with Bubble Sort

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

void bubbleSort(vector<int>& arr) {
    int n = arr.size();

    for (int pass = 0; pass < n - 1; pass++) {
        bool swapped = false;

        for (int j = 0; j < n - 1 - pass; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }

        // Early exit if no swaps in this pass — already sorted
        if (!swapped) break;
    }
}

void printArray(const vector<int>& arr) {
    for (const auto& val : arr) {
        cout << val << " ";
    }
    cout << "\n";
}

int main() {
    vector<int> data = {64, 25, 12, 22, 11};

    cout << "Before: ";
    printArray(data);
    // Before: 64 25 12 22 11

    bubbleSort(data);

    cout << "After:  ";
    printArray(data);
    // After:  11 12 22 25 64

    // Matrix operations with nested loops
    const int ROWS = 3, COLS = 3;
    int matrix[ROWS][COLS] = {{1,2,3},{4,5,6},{7,8,9}};

    cout << "Transpose:\n";
    for (int j = 0; j < COLS; j++) {
        for (int i = 0; i < ROWS; i++) {
            cout << matrix[i][j] << " ";
        }
        cout << "\n";
    }
    // 1 4 7
    // 2 5 8
    // 3 6 9

    return 0;
}

Summary

  • Traditional for: for (init; condition; update) — use when you need an index or precise iteration control
  • Range-based for: for (const auto& elem : container) — use for all standard container iteration in modern C++
  • Use const auto& to read without copying; use auto& to modify elements
  • break exits the innermost loop; continue skips to the next iteration
  • Breaking out of nested loops requires a flag variable, a function with return, or goto
  • Prefer pre-increment ++i in loops — never slower and sometimes faster for complex types
  • Cache v.size() outside the loop if calling in a tight inner loop to avoid repeated method calls

Next, we'll cover while and do-while loops — repeating code until a condition changes, without a fixed count.