C++ For Loop
A for loop lets you repeat a block of code a specific number of times without writing the same statement over and over. It bundles the loop’s initialization, its stopping condition, and its update step into one compact line, which makes it the go-to tool in C++ whenever you know (or can calculate) how many times you need to repeat something — counting, scanning arrays, building tables, and much more.
Overview / How it works
Every loop needs three things: a starting point, a condition that decides whether to keep going, and a way to move toward the end. The C++ for loop puts all three pieces in its header, which keeps loop logic in one place instead of scattered across the surrounding code. Internally, the compiler translates a for loop into the same kind of conditional jump instructions a while loop would generate — a for loop is really just syntactic sugar over a while loop, but with the counter’s lifecycle made explicit and easy to read.
The loop variable (commonly named i) is a real variable with real memory: it is created once, before the first iteration, and it lives only inside the loop’s scope if it is declared in the header (e.g. for (int i = 0; ...)). Once the loop finishes, that variable is destroyed and no longer accessible. On each pass, the CPU evaluates the condition, and if it is true, executes the loop body, then executes the increment expression, and re-checks the condition — this repeats until the condition evaluates to false, at which point control falls through to the statement after the loop.
C++ also provides a second form, the range-based for loop (introduced in C++11), which iterates directly over the elements of a container or array without you having to manage an index at all. Under the hood, the compiler still generates iterator-based traversal code, but you never see the index — you just get each element in turn.
Syntax
for (initialization; condition; update) {
// loop body — runs while condition is true
}
| Part | Purpose | Runs when |
|---|---|---|
| initialization | Declares and sets the starting value of the loop counter (e.g. int i = 0) |
Once, before the loop starts |
| condition | A boolean expression checked before each iteration (e.g. i < 10) |
Before every iteration, including the first |
| update | Modifies the counter, usually incrementing or decrementing it (e.g. i++) |
After every iteration, before the next condition check |
| body | The statement(s) to repeat | Only while the condition is true |
The range-based form looks like this:
for (declaration : range) {
// use the current element
}
Here, range is any container or array, and declaration introduces a variable bound to each element in turn, such as const string& fruit : fruits.
Examples
Example 1: Counting with a basic for loop
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 5; i++) {
cout << i << " ";
}
cout << endl;
return 0;
}
Output:
1 2 3 4 5
Here i starts at 1. Before each iteration, C++ checks i <= 5; as long as that is true, it prints i followed by a space, then increments i. Once i becomes 6, the condition is false and the loop stops — so the body ran exactly five times.
Example 2: Summing values in a vector
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> scores = {85, 92, 78, 90, 88};
int total = 0;
for (int i = 0; i < scores.size(); i++) {
total += scores[i];
}
double average = static_cast<double>(total) / scores.size();
cout << "Total: " << total << endl;
cout << "Average: " << average << endl;
return 0;
}
Output:
Total: 433
Average: 86.6
This is the classic “index into a container” pattern: i walks from 0 up to (but not including) scores.size(), and scores[i] accesses each element by position. The running total is accumulated in total, then divided by the count to get the average. Note the static_cast<double> — without it, integer division would truncate the result.
Example 3: Nested for loops for a multiplication table
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << setw(4) << i * j;
}
cout << endl;
}
return 0;
}
Output:
1 2 3
2 4 6
3 6 9
The outer loop controls the row (i), and for every single value of i, the inner loop runs completely through j from 1 to 3 before the outer loop advances. That means the body of the inner loop executes 3 × 3 = 9 times in total. setw(4) from <iomanip> right-aligns each number in a 4-character field so the table lines up neatly.
Example 4: Range-based for loop over a container
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> fruits = {"apple", "banana", "cherry"};
for (const string& fruit : fruits) {
cout << fruit << endl;
}
return 0;
}
Output:
apple
banana
cherry
Instead of writing for (int i = 0; i < fruits.size(); i++) and indexing with fruits[i], the range-based form binds fruit to each element directly. Using const string& avoids copying every string — it just refers to the existing element, which is both faster and safer for read-only iteration.
How it works step by step
- The initialization expression runs exactly once, creating and setting the loop variable.
- The condition is evaluated. If it is
false, the loop ends immediately and control jumps to the statement after the closing brace. - If the condition is
true, the loop body executes from top to bottom. - The update expression runs, modifying the loop variable (usually incrementing it).
- Control returns to step 2, and the cycle repeats until the condition becomes
false.
For a range-based for loop, the compiler generates hidden calls to begin() and end() on the container, then repeatedly dereferences and advances an iterator until it reaches the end — but you interact with none of that machinery directly.
Common Mistakes
Mistake 1: Off-by-one errors with array bounds
int arr[5] = {10, 20, 30, 40, 50};
for (int i = 0; i <= 5; i++) {
cout << arr[i] << " "; // reads arr[5], which is out of bounds!
}
Using <= with a size-based bound reads one element past the end of the array. Arrays of size 5 have valid indices 0 through 4, so the condition should use <:
int arr[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
Mistake 2: Accidentally terminating the loop with a stray semicolon
for (int i = 0; i < 5; i++);
{
cout << i << endl; // runs once, after the loop, using a stale/undeclared i
}
The semicolon right after the loop header becomes an empty statement, so the loop body is nothing — the loop just counts to 5 and does nothing five times, then the block below runs once on its own (and likely won’t even compile as written, since i is out of scope). Remove the semicolon so the intended block becomes the loop body:
for (int i = 0; i < 5; i++) {
cout << i << endl;
}
Mistake 3: Comparing size_t with a signed int
vector::size() returns an unsigned type (size_t). Writing for (int i = vec.size() - 1; i >= 0; i--) on an empty vector underflows before the loop even starts, because vec.size() - 1 wraps around to a huge unsigned number. Prefer signed loop counters compared against a signed size, or check emptiness first: for (int i = static_cast<int>(vec.size()) - 1; i >= 0; i--).
Best Practices
- Declare the loop counter inside the
forheader (for (int i = 0; ...)) so it doesn’t leak into the surrounding scope. - Prefer
<over<=when looping up to a container’s size, since valid indices run from 0 to size−1. - Use a range-based for loop whenever you don’t need the index — it is shorter, harder to get wrong, and communicates intent clearly.
- Use
const auto&(orconst T&) in range-based loops when you only read elements, to avoid unnecessary copies. - Avoid modifying the loop counter inside the body in addition to the update expression — it makes the loop’s behavior hard to predict.
- For nested loops, use different variable names (
i,j,k) so the intent of each loop level stays clear. - Use
breakto exit a loop early andcontinueto skip to the next iteration, rather than adding deeply nestedifstatements.
Practice Exercises
- Write a program that uses a
forloop to print all even numbers from 2 to 20, inclusive, separated by spaces. - Write a program that uses a
forloop to compute the factorial of a number entered by the user (for example, an input of 5 should produce 120). - Write a program with nested
forloops that prints a right triangle of asterisks with 5 rows, where row 1 has 1 asterisk, row 2 has 2 asterisks, and so on up to row 5.
Summary
- A
forloop combines initialization, condition, and update into a single header, making counted repetition concise and readable. - The loop variable is created once, checked before every iteration, and updated after every iteration until the condition becomes false.
- Range-based for loops iterate directly over container elements without manual indexing, and are preferred when you don’t need the index.
- Off-by-one errors, stray semicolons, and signed/unsigned comparisons are the most common sources of bugs in for loops.
- Nested for loops let the inner loop run to completion for every single iteration of the outer loop, which is the basis for grid- and table-style output.
