C++ course menu
C++ for Loops in Depth: Counting, Range-Based for, Nested Loops and Patterns
Go deeper with C++ for loops: counting up and down, custom steps, several loop variables, range-based for with auto and references, structured bindings, nested loops and patterns, and choosing int vs size_t for indexes.
On this page
The Loops lesson introduced the for loop. This lesson covers the variations you will use every day, and the patterns that come up again and again in exercises and interviews.
Counting in different ways#
The three parts of a for loop can do more than count up by one:
#include <iostream>
int main() {
for (int i = 10; i > 0; i -= 3) { // count down in steps of 3
std::cout << i << ' ';
}
std::cout << '\n';
for (int i = 1; i <= 100; i *= 2) { // powers of two
std::cout << i << ' ';
}
std::cout << '\n';
}10 7 4 1
1 2 4 8 16 32 64Several loop variables#
The initialization and update parts can handle more than one variable, separated by commas. A common use is walking from both ends towards the middle:
#include <iostream>
#include <string>
int main() {
std::string word = "level";
bool palindrome = true;
for (int left = 0, right = word.size() - 1; left < right; ++left, --right) {
if (word[left] != word[right]) {
palindrome = false;
}
}
std::cout << word << (palindrome ? " is" : " is not") << " a palindrome\n";
}level is a palindromeRange-based for in detail#
The range-based for visits every element of a container:
#include <iostream>
#include <vector>
int main() {
std::vector<int> prices = {120, 45, 300};
for (int p : prices) { // p is a copy
std::cout << p << ' ';
}
std::cout << '\n';
for (int& p : prices) { // p refers to the real element
p += 10;
}
for (const auto& p : prices) { // read-only, no copies
std::cout << p << ' ';
}
std::cout << '\n';
}120 45 300
130 55 310Three forms to remember:
Looping over a map with structured bindings#
When each element is a pair, such as in a std::map, structured bindings (C++17) unpack it into two names:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> stock = {{"pens", 40}, {"books", 12}, {"bags", 5}};
for (const auto& [item, count] : stock) {
std::cout << item << ": " << count << '\n';
}
}bags: 5
books: 12
pens: 40A std::map keeps its keys sorted, which is why bags comes first. See map.
Index loops and size_t#
When you need the position, loop with an index. A container's size() is an unsigned type (std::size_t), so comparing it with an int triggers a signed/unsigned warning. Use std::size_t for the index:
#include <iostream>
#include <vector>
int main() {
std::vector<int> scores = {72, 88, 95};
for (std::size_t i = 0; i < scores.size(); ++i) {
std::cout << "#" << i + 1 << ": " << scores[i] << '\n';
}
}#1: 72
#2: 88
#3: 95Nested loops#
A loop inside a loop runs the inner loop completely for every step of the outer loop:
#include <iostream>
int main() {
for (int row = 1; row <= 3; ++row) {
for (int col = 1; col <= 4; ++col) {
std::cout << row * col << '\t';
}
std::cout << '\n';
}
}1 2 3 4
2 4 6 8
3 6 9 12The inner body runs 3 × 4 = 12 times. Nested loops multiply quickly: two loops of 1000 steps each run the inner body a million times.
Printing patterns#
Pattern problems are classic practice for nested loops. The inner loop's limit depends on the outer variable:
#include <iostream>
int main() {
int height = 4;
for (int row = 1; row <= height; ++row) {
for (int i = 0; i < height - row; ++i) std::cout << ' ';
for (int i = 0; i < 2 * row - 1; ++i) std::cout << '*';
std::cout << '\n';
}
} *
***
*****
*******Row r has height - r spaces, then 2r - 1 stars.
Common loop patterns#
#include <iostream>
#include <vector>
int main() {
std::vector<int> values = {14, 3, 27, 8, 19};
int sum = 0;
int largest = values[0];
int even_count = 0;
for (int v : values) {
sum += v;
if (v > largest) largest = v;
if (v % 2 == 0) ++even_count;
}
std::cout << "Sum: " << sum << '\n';
std::cout << "Largest: " << largest << '\n';
std::cout << "Even numbers: " << even_count << '\n';
}Sum: 71
Largest: 27
Even numbers: 2Accumulate (sum), track the best so far (largest), and count matches: these three patterns solve a large share of loop problems. The standard library also has ready-made versions (std::accumulate, std::max_element, std::count_if) covered in Algorithms.
Common mistakes#
Practice#
Practice exercise
Print this right-angled number triangle using nested loops:
1
1 2
1 2 3
1 2 3 4Show solution
#include <iostream>
int main() {
for (int row = 1; row <= 4; ++row) {
for (int n = 1; n <= row; ++n) {
std::cout << n;
if (n < row) std::cout << ' ';
}
std::cout << '\n';
}
}1
1 2
1 2 3
1 2 3 4Practice exercise
Given std::vector<int> marks = {56, 91, 78, 34, 88};, use a range-based for to print the average (as a decimal) and how many marks are 75 or above.
Show solution
#include <iostream>
#include <vector>
int main() {
std::vector<int> marks = {56, 91, 78, 34, 88};
int total = 0;
int high = 0;
for (int m : marks) {
total += m;
if (m >= 75) ++high;
}
double average = static_cast<double>(total) / marks.size();
std::cout << "Average: " << average << '\n';
std::cout << "75 or above: " << high << '\n';
}Average: 69.4
75 or above: 3Key takeaway#
Finished this lesson?