Nested loops are loops within the bodies of other loops. For each iteration of the outer loop, the inner loop runs from the start to the end of its own statements.
These structures are useful for generating each combination possible from two separate sets of data or for displaying a rectangular grid of output.
// Increment the value before the expression uses it
for (int row{0}; row < 3; ++row)
{
for (int column{0}; column < 4; ++column)
{
std::cout << row << "," << column << ' ';
}
std::cout << '\n';
}
One loop typically handles one of the dimensions, like rows, while the inner loop goes through each of the options for that row.
Once the inner loop is completed, the next iteration of the outer loop is begun and again the entire set of inner statements run. A nested structure with 3 outer iterations and 4 inner iterations generates 12 combinations, displayed across 3 rows.
The interaction of iterations goes as follows: An outer loop controls how many times the inner loop is performed. The inner loop is restarted each time execution enters into the body of the outer loop and performs its iterations until its own condition evaluates to false, then the rest of the outer loop's body (if any) is carried out.
// Stop before the upper boundary is reached
for (int outer{0}; outer < 3; ++outer)
{
for (int inner{0}; inner < 4; ++inner)
{
std::cout << outer << "," << inner << '\n';
}
std::cout << "Inner loop completed\n";
}
The total number of executions of the inner loop body is generally found by multiplying the iterations of the outer loop by the iterations of the inner loop.
If there are 3 iterations on the outer loop and 4 on the inner, the inner loop body will execute 12 times. Changing either range alters the total amount of work. Doubling the outer range will cause the inner loop to execute exactly twice as many times.
Doubling the inner range will cause the inner loop body to work through 2 as many inner values each time outer-range is performed. Any type of loop may be used nested inside any other loop.
A for loop may be placed inside a while loop, a do-while loop inside a for loop, or another while loop inside a while loop.
int outer{0};
while (outer < 2)
{
for (int inner{0}; inner < 3; ++inner)
{
std::cout << outer << "-" << inner << '\n';
}
++outer;
}
The loops must always have conditions and be set up to eventually complete, and each controlling loop variable will affect only one of the loops; the outer variable will not change the values of the inner variables during the execution of the inner loop, nor will the inner variables affect the outer variable during their execution.
In nested loops, both break and continue only affect the one innermost loop that contains the statement. When break is within the inner loop, it immediately exits the inner loop and continues with the first statement after the inner loop (though it still is within the outer loop's body if there is anything that follows that).
Thus, the outer loop cannot possibly terminate with a break within an inner loop. When continue is within the inner loop, it immediately starts the next iteration of the inner loop.
for (int outer{0}; outer < 3; ++outer)
{
for (int inner{0}; inner < 5; ++inner)
{
if (inner == 2)
{
// Leave the loop or switch immediately
break;
}
std::cout << outer << "," << inner << '\n';
}
std::cout << "Outer iteration completed\n";
}
for (int row{0}; row < 3; ++row)
{
for (int column{0}; column < 4; ++column)
{
if (column == 2)
{
// Skip the rest of this iteration
continue;
}
std::cout << row << "," << column << ' ';
}
std::cout << '\n';
}
The outer loop continues unaffected in both circumstances.
A variable that was declared by the outer loop is visible to the inner loop, since the inner loop is a part of the outer loop's body. An inner variable can only be seen up until the end of the inner loop.
It's better to name the two variables differently (i.e. An outer variable named 'x' and an inner variable named 'i'), since it's easy to lose track of which loop you're dealing with, especially if they have the same name.
for (int x{0}; x < 3; ++x)
{
for (int i{0}; i < 4; ++i)
{
std::cout << x << "," << i << '\n';
}
// x is visible here.
// i is not visible here.
}
In fact, if they have different names they're even more easily remembered. Multiple loops can cause a significant increase in executed statements. With two 10-iteration loops, there are 100 executions of the inner loop body.
With three 10-iteration loops, there are 1000 statements.
for (int first{0}; first < 10; ++first)
{
for (int second{0}; second < 10; ++second)
{
for (int third{0}; third < 10; ++third)
{
std::cout << first << "," << second << "," << third << '\n';
}
}
}
This increase occurs exponentially with added loops, and while it's not a problem if the statements executed are minor, it could be a significant issue if they involve difficult processes. The most practical approach is to keep the number of iterations relatively low in each loop when learning, just to simplify inspecting the output and execution of the program.
A nested structure can print a multiplication table by making one variable's iterations one number and the other variable's iterations the second. Another structure with two while loops can visit every combination between two rather short ranges.
One still using a continue inside the inner loop structure can skip a row entirely while the inner loop continues iterating without interruption.
#include <iostream>
int main()
{
// Include the upper boundary in this loop
for (int row{1}; row <= 3; ++row)
{
for (int column{1}; column <= 3; ++column)
{
std::cout << row * column << ' ';
}
std::cout << '\n';
}
int outer{0};
while (outer < 3)
{
int inner{0};
while (inner < 2)
{
std::cout << "Pair " << outer << "," << inner << '\n';
++inner;
}
++outer;
}
for (int row{0}; row < 4; ++row)
{
for (int column{0}; column < 4; ++column)
{
if (column == 2)
{
continue;
}
std::cout << row << column << ' ';
}
std::cout << '\n';
}
}
In each example, the inner loop terminates before the outer loop begins its next iteration. The outer loop dictates the situation while the inner loop enumerates and carries out whatever is needed for that situation.
Exercise: Nested LoopsStarter code, hints and solutionOpenClose
Practice: Nested Loops
Work through the mission, reveal hints only when needed, and compare your result with the model solution.
Your challenge
Add exactly one outer iteration to the first multiplication-table loop, keeping three columns. Predict the added row and distinguish outer from inner iterations.
Mission checklist
- Run the starter as a baseline, then predict the change requested in Your challenge.
- Make that change in your editor; use the lesson-specific hints if you need help.
- Compile and run your answer, then compare with the worked solution and explain the result.
Starter program
This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.
#include <iostream>
int main()
{
// Include the upper boundary in this loop
for (int row{1}; row <= 3; ++row)
{
for (int column{1}; column <= 3; ++column)
{
std::cout << row * column << ' ';
}
std::cout << '\n';
}
int outer{0};
while (outer < 3)
{
int inner{0};
while (inner < 2)
{
std::cout << "Pair " << outer << "," << inner << '\n';
++inner;
}
++outer;
}
for (int row{0}; row < 4; ++row)
{
for (int column{0}; column < 4; ++column)
{
if (column == 2)
{
continue;
}
std::cout << row << column << ' ';
}
std::cout << '\n';
}
}Hints
For the multiplication table, each outer row starts a complete inner column loop. Adding one row adds several printed products, not just one product.
In the while-loop pair, inner is reset to 0 inside each outer iteration. In the last example, continue applies only to the inner column loop.
Worked solution
The first outer row loop now runs through row 4 instead of row 3: exactly one extra outer iteration. That iteration performs the three inner multiplications, adding the row 4 8 12. The other loop examples are unchanged.
#include <iostream>
int main()
{
// Include the upper boundary in this loop
for (int row{1}; row <= 4; ++row)
{
for (int column{1}; column <= 3; ++column)
{
std::cout << row * column << ' ';
}
std::cout << '\n';
}
int outer{0};
while (outer < 3)
{
int inner{0};
while (inner < 2)
{
std::cout << "Pair " << outer << "," << inner << '\n';
++inner;
}
++outer;
}
for (int row{0}; row < 4; ++row)
{
for (int column{0}; column < 4; ++column)
{
if (column == 2)
{
continue;
}
std::cout << row << column << ' ';
}
std::cout << '\n';
}
}
Example output
1 2 3
2 4 6
3 6 9
4 8 12
Pair 0,0
Pair 0,1
Pair 1,0
Pair 1,1
Pair 2,0
Pair 2,1
00 01 03
10 11 13
20 21 23
30 31 33
0 comments
No comments yet. Be the first.