Why this matters
Many loops do not count 1, 2, 3, 4. Some visit every second item. Some start at the end of a list and walk back to the front. Some double a number each time. A beginner often gets these loops wrong in one of two ways: the loop runs one time too many, or it never stops. Both mistakes come from the same place, which is the third part of the for statement, the change that happens after each pass.
This lesson covers that third part, called the step here, and the test that decides when the loop ends. It also covers the value the counter holds after the loop finishes, which beginners often guess wrong. The lesson on the for loop explains the three parts in general. This lesson looks only at the step and what it does to the range of values.
The step: how far the counter moves
In a for loop, the third part is the change that runs after the body on each pass. In the earlier lessons it was always ++i, which adds 1. The change can be any update. These are common ones:
++iori++: add 1. The step is 1.i += 2: add 2. The step is 2.--iori--: subtract 1. The step is -1.i += stepwherestepis a variable: the step is whatever the variable holds.i *= 2: multiply by 2. This is not an addition, so the values do not form an evenly spaced list.
Three parts decide the values a loop visits: the starting value, the step, and the test that decides how far the counter may go. Keep all three in view when you read a loop.
Counting by 2
Here is a loop that visits the even numbers from 0 to 8.
#include <iostream>
int main() {
for (int i = 0; i < 10; i += 2) {
std::cout << i << ' ';
}
std::cout << '\n';
}Output:
0 2 4 6 8 The counter starts at 0. The test asks whether the counter is less than 10. The update adds 2 after each pass. So the body sees 0, then 2, then 4, then 6, then 8. After 8 the counter becomes 10, and the test i < 10 is false, so the loop stops.
Read the test and the step together. The test says "stop before 10", and the step is 2, so the loop visits every second number that is below 10. If the test were i <= 10, the loop would also visit 10.
Use the same reading for any step. Write down the start, then the values you get by adding the step, then stop at the first value that fails the test. For a start of 0, a test of i < 10, and a step of 2, the values are 0, 2, 4, 6, 8, and 10 is the value that fails.
The value after the loop
Beginners often assume that the counter holds the end value after the loop. That is not what happens. After the loop stops, the counter holds the first value that failed the test. That value was never used by the body.
Here is a program that prints the last value the body saw, and the value of the counter after the loop. The counter is declared before the loop, so it can be read after the loop finishes.
#include <iostream>
int main() {
int last_used = 0;
int i = 0;
for (i = 0; i < 10; i += 2) {
last_used = i;
}
std::cout << "last value used: " << last_used << '\n';
std::cout << "value of i after the loop: " << i << '\n';
int j = 0;
for (j = 0; j < 10; j += 3) {
std::cout << j << ' ';
}
std::cout << '\n';
std::cout << "value of j after the loop: " << j << '\n';
}Output:
last value used: 8
value of i after the loop: 10
0 3 6 9
value of j after the loop: 12With step 2, the body last ran with 8, and the counter ended at 10. With step 3, the body last ran with 9, and the counter ended at 12. The value 12 was never printed. It is the first value of the sequence 0, 3, 6, 9, 12 that is not below 10.
This gives a rule you can check by hand. If the loop starts at start, steps by step, and runs while the counter is less than end, then after the loop the counter holds the first value of start, start + step, start + 2 * step, and so on that is not less than end. The lesson on half-open ranges explains why the end value is left out of the test and why this rule is useful.
Counting down by 1
To count down, the start is high, the test checks the lowest value you want, and the step is -1. Here is a loop that prints 5 down to 1.
#include <iostream>
int main() {
for (int i = 5; i >= 1; --i) {
std::cout << i << ' ';
}
std::cout << '\n';
}Output:
5 4 3 2 1 The test is i >= 1, not i > 1. The test has to keep the last value you want. With i > 1, the body would never see 1, and the output would stop at 2. Ask the same question as before: which value is the first one that fails the test? Here, 0 is the first value that fails i >= 1. So the counter ends at 0.
Reverse order through a list
The most common countdown in real code walks through a list from its last position to its first. In C++, a vector's positions run from 0 to size() - 1. A natural first attempt looks like this:
for (std::size_t i = values.size() - 1; i >= 0; --i) {
// use values[i]
}This loop has a serious problem. The type std::size_t is an unsigned integer. An unsigned integer cannot hold a negative number. When i is 0 and you subtract 1, the result does not become -1. It wraps around to the largest value the type can hold. The test i >= 0 is then always true, and the loop never stops.
Here is a version of that loop with a line that stops it after six passes, so that you can see what happens. The shown == 6 check is only there for this demonstration.
#include <cstddef>
#include <iostream>
int main() {
std::size_t n = 3;
int shown = 0;
// Intended: visit 2, 1, 0 and stop.
// The "shown == 6" check only ends the demo; it is not part of the fix.
for (std::size_t i = n - 1; i >= 0; --i) {
std::cout << i << ' ';
if (++shown == 6) {
break;
}
}
std::cout << '\n';
}Output:
2 1 0 18446744073709551615 18446744073709551614 18446744073709551613 The loop visits 2, 1 and 0 as intended, then it goes on to the huge numbers that the wrap-around produces. Under -Wall, g++ 15 printed no warning for this line. The compiler does not catch this mistake, so you have to reason about it yourself.
The safe way to count down over positions is to move the test into the step, so that the test sees the value before it is reduced:
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
std::vector<int> values{10, 20, 30};
for (std::size_t i = values.size(); i-- > 0;) {
std::cout << "index " << i << " value " << values[i] << '\n';
}
}Output:
index 2 value 30
index 1 value 20
index 0 value 10The expression i-- gives the old value of i and then lowers i by 1. In the test i-- > 0, the comparison uses the old value. The first test sees 3 and is true, and i becomes 2. The body uses values[2]. The next test sees 2, which is true, and i becomes 1. This repeats until the test sees 0, which is false, and the loop stops. The body never reads a position that does not exist.
Another correct form is for (std::size_t i = values.size(); i > 0; --i), and then the body reads values[i - 1]. Either form is fine. Choose the one that your team already uses, and keep it the same across the code.
A variable step
The step does not have to be a number written in the code. It can be a variable, for example a value read from the user or computed by an earlier part of the program. The loop then does what the variable says.
#include <iostream>
// Prints start, start + step, ... for every value below end.
// A step of zero or less would never reach end, so the function refuses it.
bool print_range(int start, int end, int step) {
if (step <= 0) {
std::cout << "step must be positive, got " << step << '\n';
return false;
}
for (int i = start; i < end; i += step) {
std::cout << i << ' ';
}
std::cout << '\n';
return true;
}
int main() {
print_range(3, 20, 4);
print_range(3, 20, 0);
print_range(3, 20, -2);
}Output:
3 7 11 15 19
step must be positive, got 0
step must be positive, got -2The first call works. The values are 3, 7, 11, 15 and 19, and the next value, 23, is not below 20. The other two calls are refused before the loop starts. The reasons are different, so it is worth understanding each one.
A step of 0 makes the loop run forever. The counter never changes, so i < end stays true, and the body runs again and again. This is one of the most common reasons a program hangs.
A negative step with a test of < also never ends. The counter goes down, so it stays below end forever. Eventually the counter would run past the smallest value an int can hold. Signed overflow in C++ is undefined behaviour, which means the language does not say what the program does. The program has no defined result. The simplest protection is the check at the top of the function, which stops the loop before it starts.
You can make a negative step work by changing the test as well. For example, to count down from 19 to 3, you could write for (int i = 19; i >= 3; i -= 4). The test and the step must agree on the direction. If the step goes down, the test must check a lower limit.
Doubling and other changing steps
A step does not have to add a fixed amount. You can multiply, and the values then grow faster than a straight line. Here is a loop that doubles a number until it is greater than a bound.
#include <iostream>
int main() {
// Doubling. The bound 1000000000 is chosen so that the last value that passes
// the test (2^29 = 536870912) is followed by 2^30, which fails the test
// and is never printed. The doubled value is never allowed to overflow int.
int count = 0;
int last = 0;
for (int p = 1; p <= 1000000000; p *= 2) {
last = p;
++count;
}
std::cout << "count = " << count << ", last value used = " << last << '\n';
}Output:
count = 30, last value used = 536870912Thirty values pass the test: 2 to the power 0 up to 2 to the power 29. The loop stops at 2 to the power 30, which is 1073741824 and is above the bound. The number of passes is a count of doublings, so the loop finishes in about 30 steps even though the values reach a billion.
The danger is the top of the range. An int on most current machines holds values up to 2147483647. If the test were p <= 2000000000, the value 2 to the power 30 would pass the test. The next doubling would produce 2 to the power 31, which does not fit in an int, and the result is undefined. Before you write a multiplying step, check that the last value you need, multiplied by the step, still fits in the type. If it does not, use long long, or stop the loop earlier.
Working out the count without running the loop
Sometimes you need to know how many values a loop visits, and you do not want to run it. For a loop that counts up, with a step greater than 0, and an end value greater than the start, the count is the step count rounded up:
count = ceil((end - start) / step)
In integer arithmetic, C++ gives you this with one line, because integer division drops the fraction. Adding step - 1 before dividing rounds up:
(end - start + step - 1) / stepThe program below checks this formula against the real loop for six cases. The loop is the ground truth, and the formula is what you would use to avoid running the loop.
#include <iostream>
// Counts the values that "for (i = start; i < end; i += step)" visits, by running the loop.
int count_by_loop(int start, int end, int step) {
int count = 0;
for (int i = start; i < end; i += step) {
++count;
}
return count;
}
// Formula for step > 0 and end > start: ceil((end - start) / step),
// written with integer division.
int count_by_formula(int start, int end, int step) {
return (end - start + step - 1) / step;
}
int main() {
struct Case {
int start;
int end;
int step;
};
const Case cases[] = {
{0, 10, 2},
{0, 10, 3},
{3, 20, 4},
{1, 100, 7},
{5, 6, 10},
{0, 9, 3},
};
for (const Case& c : cases) {
std::cout << "start=" << c.start << " end=" << c.end << " step=" << c.step
<< " loop=" << count_by_loop(c.start, c.end, c.step)
<< " formula=" << count_by_formula(c.start, c.end, c.step) << '\n';
}
}Output:
start=0 end=10 step=2 loop=5 formula=5
start=0 end=10 step=3 loop=4 formula=4
start=3 end=20 step=4 loop=5 formula=5
start=1 end=100 step=7 loop=15 formula=15
start=5 end=6 step=10 loop=1 formula=1
start=0 end=9 step=3 loop=3 formula=3Take the case 0, 10, 3 by hand. The values are 0, 3, 6 and 9, so there are 4. The formula gives (10 - 0 + 3 - 1) / 3, which is 12 / 3, which is 4. Now take 0, 9, 3. The values are 0, 3 and 6, because 9 is not below 9. The formula gives (9 + 2) / 3, which is 11 / 3, and integer division gives 3. Both agree.
The formula only applies when the end is greater than the start. If the end is not greater than the start, the loop runs zero times, and the answer is 0. Do not use the formula in that case. For example, with start 6, end 2 and step 2, the formula gives (-4 + 1) / 2, which is -3 / 2, and C++ rounds that toward zero to -1. A count cannot be negative, so that answer is wrong. The check if (end <= start) return 0; before the formula fixes it.
Common mistakes
Using != with a step that skips the end
A test of i != 9 looks like it should stop the loop at 9. With a step of 2, the counter visits 0, 2, 4, 6 and 8, then 10, and it never equals 9. The loop does not stop. The program below uses a counter to end the demonstration after eight passes, so you can see the values.
#include <iostream>
int main() {
// Start 0, end 9, step 2 with "!=" as the test. 9 is never reached.
// The "passes == 8" check only ends this demo.
int passes = 0;
for (int i = 0; i != 9; i += 2) {
std::cout << i << ' ';
if (++passes == 8) {
break;
}
}
std::cout << "\npasses = " << passes << '\n';
}Output:
0 2 4 6 8 10 12 14
passes = 8The counter went past 9 and kept going. Use < or <= in the test when the step can skip the end value. Use != only when you are sure the counter lands on the end value exactly.
A step of zero or the wrong sign
A step of 0 never changes the counter, so the loop runs forever. A step with the wrong sign, such as a negative step with a test of <, moves the counter away from the end. The output below is a list of values that keep going down, and the loop has no defined end. The check if (++passes == 6) only stops this demo.
#include <iostream>
int main() {
// Start 3, end 20, step -2 with "<" as the test. i gets smaller, so i < 20 stays true.
int passes = 0;
for (int i = 3; i < 20; i += -2) {
std::cout << i << ' ';
if (++passes == 6) {
break;
}
}
std::cout << "\npasses = " << passes << '\n';
}Output:
3 1 -1 -3 -5 -7
passes = 6Check the sign of the step against the direction of the test before you run the loop. If the test is < or <=, the step must be positive.
Expecting the counter to equal the end
The earlier example showed the counter at 12 after a loop that was meant to stop at 10. Many beginners write code that expects the counter to be exactly the end value, and then use the counter as a result. If you need the last value the body used, store it in a second variable inside the loop, as the earlier example did with last_used.
An off-by-one countdown
A countdown with the test i > 1 skips the value 1. The loop prints 5, 4, 3 and 2, and never prints 1. A countdown that should include 0 needs i >= 0. The fix is always the same: the test must allow the last value you want, so write the test by asking which value must still pass.
The unsigned countdown
This is the mistake shown in the reverse example. The loop for (std::size_t i = n - 1; i >= 0; --i) does not stop, because an unsigned counter cannot go below 0. The compiler did not warn about it under -Wall, but -Wextra does. Use the form i-- > 0 or a signed int counter for countdowns. The signed form has its own limits, but it can represent -1, so the test i >= 0 stops at the right place.
Practice
Exercise 1
Write a loop that prints 10, 8, 6, 4, 2 and 0, each on its own line.
Show the answer
Use a start of 10, a test that keeps 0, and a step of -2. The loop is for (int i = 10; i >= 0; i -= 2) { std::cout << i << '\n'; }. The values are 10, 8, 6, 4, 2 and 0. The test i >= 0 keeps 0, and the next value, -2, fails the test.
Exercise 2
What does this loop print, and what is the value of i after the loop?
for (int i = 1; i < 20; i += 4) {
std::cout << i << ' ';
}Show the answer
It prints 1 5 9 13 17, with a space after each number. The values are 1, 5, 9, 13 and 17. The next value, 21, is not below 20, so the loop stops with i equal to 21. The value 21 is the first value of the sequence that fails the test.
Exercise 3
Use the formula from this lesson to say how many values the loop for (int i = 2; i < 31; i += 7) visits. Then check your answer by listing the values.
Show the answer
The formula gives (31 - 2 + 7 - 1) / 7, which is 35 / 7, which is 5. The listed values are 2, 9, 16, 23 and 30. Five values is the right count. The end is written as 31 because the test is <. A test of <= 30 would give the same five values, and the formula would then use an end of 31.
Exercise 4
Write a loop that prints every value in a std::vector<int> from last to first, and does not use an unsigned counter that can wrap around. Test it on a vector that holds 4, 8 and 15, and on an empty vector.
Show the answer
One correct form is for (std::size_t i = values.size(); i > 0; --i) { std::cout << values[i - 1] << '\n'; }. The counter starts at the size, which is one more than the last position, and the body reads values[i - 1]. The test i > 0 stops before the counter reaches 0, so values[i - 1] never reads a position below 0. For the vector 4, 8, 15 the output is 15, 8 and 4. For an empty vector the test is false on the first check, and nothing prints. The version i-- > 0 from this lesson also works and prints the same values.
Exercise 5
A student writes this loop to visit each position of a vector in reverse. Explain what goes wrong for a vector that is empty, and for a vector that is not empty.
for (std::size_t i = values.size() - 1; i >= 0; --i) {
std::cout << values[i] << '\n';
}Show the answer
For an empty vector, values.size() is 0, and 0 minus 1 wraps around to the largest std::size_t value. The loop starts at a huge position and reads values[i] far outside the vector. That is undefined behaviour, and the program may crash or print garbage. For a vector that is not empty, the first problem is the test i >= 0. An unsigned counter is always at least 0, so the test is always true. When i reaches 0 and the step subtracts 1, i wraps around and the loop continues with huge positions, which is the failure shown in this lesson. Use the form for (std::size_t i = values.size(); i-- > 0;), which fixes both problems, because it never subtracts from zero.
Summary
- The third part of a
forloop, the step, decides how the counter moves, and it can be an addition, a subtraction, or a multiplication. - When a loop ends, the counter holds the first value that failed the test, which the body never used.
- A countdown needs a test that keeps the last value you want, and an unsigned counter cannot count below zero.
- A step of zero, or a step whose sign does not match the test, makes the loop run without end.
- The number of values for a counting-up loop is (end - start + step - 1) / step, when the end is greater than the start.