Loops
Learn how we can use loops to iterate over a block of code, executing it as many times as needed.
So far, the code we have written has been able to create programs that execute a single stream of statements, and then exit.
In this lesson, we will learn about loops. Loops will allow us to repeatedly execute a block of code, until some condition within our program changes.
While Loops
The most basic form of loop is the while
loop. It executes a block of code as long as a boolean expression remains true
.
The basic structure of a while
loop looks like this:
while (someBoolean) DoSomething();
Where we need to have multiple statements executed in our loop, we can wrap them with {
and }
, like in the following example:
while (someBoolean) {
DoSomething();
DoAnotherThing();
}
For example, to write the numbers from 1 to 10 to the console using a while
loop, we could do this:
#include <iostream>
using namespace std;
int main(){
int Number{1};
while (Number <= 10) {
cout << Number << ", ";
++Number;
}
}
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
The syntax of a while
loop is very similar to the if
statements we have seen in the past. We have the keyword while
, a boolean expression inside (
and )
, and a statement to execute.
The main difference between an if
and a while
is that:
- an
if
statement will execute once if the provided expression is true - a
while
statement will execute continuously, as long as the provided expression remains true.
If the condition provided to a while
loop is false
at the point the while
loop attempts to start, the code inside the block is not executed - not even once.
The following code will not log out anything:
while (false) {
cout << "Can't see me!";
}
Test your Knowledge
Using while
Loops
What will be the value of i
after running this code?
int i { 0 };
while (i <= 5) {
++i;
}
Do While Loops
A do while
loop is very similar to a while
loop, with two key differences. The first is that the syntax is slightly different - the condition comes at the end of the block:
do {
// Body here
} while (shouldLoop);
To log out the numbers from 1-10 using a do while
loop, we could do this:
#include <iostream>
using namespace std;
int main(){
int Number{1};
do {
cout << Number << ", ";
++Number;
} while (Number <= 10);
}
The second difference between a while
and a do while
is that the code inside the block of a do while
loop will always be executed at least once, even if the boolean is initially false
.
The following code will log out Hello World!
one time.
do {
cout << "Hello World!" << endl;
} while (false);
Test your Knowledge
Using do while
Loops
What will be the value of i
after running this code?
int i = 0;
do {
++i;
} while (i < 0);
For Loops
The third type of loop, the for
loop, is slightly more complicated. The basic structure is:
for (initialize; condition; update) {
// Code here
}
We have three components inside the (
and )
of the for
loop. These are:
- Initialize - this statement is executed a single time, before our loop starts
- Condition - this is the boolean expression that indicates if our loop should continue
- Update - this code is executed at the end of each loop iteration
These three components are separated by a semicolon ;
Let's see an example of how we can use this to log out the numbers from 1 to 10:
#include <iostream>
using namespace std;
int main(){
for (int Number{1}; Number <= 10; ++Number) {
cout << Number << ", ";
}
}
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
Test your Knowledge
Using for
Loops
What will be the value of i
after running this code?
int Number { 0 };
for (int i { 0 }; i < 10; ++i) {
Number++;
};
With for
loops, all three components inside the (
and )
are optional. We can omit the ones we don't need.
However, we still do need to keep the semicolons, to disambiguate which components we are using.
For example, to omit the 1st and 3rd components, we could do this:
int Number { 1 };
for ( ; Number < 10 ; ) {
cout << Number << ", ";
++Number;
}
The above is rather messy code. We should just use a while
loop in that scenario, but it is possible to use a for
loop. We can even remove the conditional check:
int Number { 1 };
for ( ; ; ) {
cout << Number << ", ";
++Number;
}
This code will compile, but you may see a problem with it. We're not specifying when the loop should stop, so the loop will never stop.
Infinite Loops
Inevitably, we will make a mistake when writing a loop that causes the loop to never end. For example:
int Number { 1 };
while (Number <= 10) {
cout << Number << ", ";
// Oops! we forgot to increment Number
}
Because the body of our loop does not affect the boolean expression that controls when our loop ends, this loop will not end under normal circumstances.
Number
will always be <= 10
. This is a fairly common scenario, called an infinite loop.
When we introduce an infinite loop, the remedy is usually just to force our program to close. For programs running in our terminal that are stuck in an infinite loop, we should still be able to close them from the title bar.
If that doesn't work, or we're creating a program that doesn't have the title bar, we can generally force our programs to close from our operating system - eg, ctrl + alt + delete in Windows, or Force Quit on macOS.
Loop Scopes
The blocks created by our loop statements behave like any blocks we've seen, such as those associated with functions and if
statements.
They have their scope, inside which we can create variables and call functions.
Scope
Learn more about how and when we can access variables, by understanding the importance of the scope in which they exist.
The same scope rules apply - for example, code within the blocks created by our loop can access variables in parent scopes, as we've seen:
int Number { 1 };
while (Number <= 10) {
// Accessing a parent scope
cout << Number << ", ";
}
However, if we create a variable within our loop, code outside of the loop will not be able to access it, for the exact same reasons we covered earlier in our lesson on scopes:
#include <iostream>
using namespace std;
int main() {
while (true) {
int Number{1};
//
}
cout << Number;
}
error: 'Number': undeclared identifier
Finally, it's worth acknowledging that every iteration of our loop creates a new block scope.
Every iteration of our previous loop creates a new variable called Hello
, in a new block created just for that iteration of the loop.
Any variables that were declared within an iteration's block are no longer accessible once that iteration completes.
Nesting Conditionals within Loops
Within the block of a loop, we can apply most of the techniques we've been doing so far. For example, we can create variables, call functions, and use conditional logic.
For example, to log out the numbers from 1-10, but to treat one number differently, we could nest an if
statement inside our loop:
#include <iostream>
using namespace std;
int main(){
int Number{1};
while (Number <= 10) {
if (Number == 5) {
cout << "Five" << ", ";
} else { cout << Number << ", "; }
++Number;
}
}
1, 2, 3, 4, Five, 6, 7, 8, 9, 10,
Nesting Loops within Loops
We can also nest loops within loops. For example, the following logs out a 3x3 grid using nested loops:
#include <iostream>
using namespace std;
int main(){
for (int Row{1}; Row <= 3; ++Row) {
for (int Col{1}; Col <= 3; ++Col) {
cout << "Row " << Row << ", "
<< "Col " << Col << " | ";
}
cout << '\n';
}
}
Row 1, Col 1 | Row 1, Col 2 | Row 1, Col 3 |
Row 2, Col 1 | Row 2, Col 2 | Row 2, Col 3 |
Row 3, Col 1 | Row 3, Col 2 | Row 3, Col 3 |
In the above example, we have an outer loop that logs out 3 rows, with a new line - \n
- after each one.
Within each iteration of this outer loop, we have an inner loop, which also performs 3 iterations. Each iteration logs out a cell of that row.
The net effect is our outer loop iterates 3 times, each causing 3 iterations of the inner loop.
So, the code within the inner loop is invoked 9 times in total.
In the next lesson, covering the modulus operator, we will see a slightly simpler way of implementing this behavior.
The continue
Statement
We can use the continue
keyword within the body of a loop to give us more control over its execution. When we call continue
, the loop will skip over the remaining code for this iteration, and continue onwards to the next.
If the condition that controls the loop is no longer true
, the loop will instead end.
The continue
statement is most typically used in a conditional block within our loop.
For example, to log the numbers from 1-10, whilst skipping over 5, we could write this code:
#include <iostream>
using namespace std;
int main(){
int Number{0};
while (Number < 10) {
++Number;
if (Number == 5) continue;
cout << Number << ", ";
}
}
1, 2, 3, 4, 6, 7, 8, 9, 10,
Test your Knowledge
Using the continue
Statement
What will be the value of Number
after running this code?
int Number { 0 };
for (int i = 0; i < 10; ++i) {
if (i == 5) continue;
Number++;
};
The break
Statement
The break
keyword can be called within the body of a loop, to exit the loop entirely, and to stop iterating. Similar to how continue
was used, break
is generally most useful within a conditional in our loop.
For example, to limit a while
loop to 100 iterations, regardless of whether the condition is true
, we could do this:
#include <iostream>
using namespace std;
int main(){
int RemainingIterations{100};
while (true) {
--RemainingIterations;
if (RemainingIterations <= 0) break;
}
}
The break
statement gives us an alternative way to control when our loops end. In some situations, using this approach can be an easier way to control program flow than using a boolean expression. We're free to use whatever approach makes most sense for our specific problem.
Test your Knowledge
Using the break
Statement
What will be the value of i
after running this code?
int i { 0 };
while (i >= 0) {
if (i == 5) break;
++i;
};
Returning from Loops
Within a loop, we can also use a return
statement to end the execution of the function where our loop is running.
This act also implicitly ends our loop, as if we called break
on it.
#include <iostream>
using namespace std;
void Log(){
for (int i{1}; i < 10; ++i) {
cout << i << ", ";
if (i == 5) return;
}
}
int main(){ Log(); }
1, 2, 3, 4, 5,
Note, however, that there is a subtle difference between return
and break
break
will end the loopreturn
will end both the loop and function
This has implications if our function has further code after the end of the loop. In the following example, our program will never say "Goodbye" - that is effectively unreachable code:
#include <iostream>
using namespace std;
void Log(){
for (int i{1}; i < 10; ++i) {
cout << i << ", ";
if (i == 5) return;
}
cout << "Goodbye";
}
int main(){ Log(); }
1, 2, 3, 4, 5,
But replacing the return
statement with break
will have the desired effect:
#include <iostream>
using namespace std;
void Log(){
for (int i{1}; i < 10; ++i) {
cout << i << ", ";
if (i == 5) break;
}
cout << "Goodbye";
}
int main(){ Log(); }
1, 2, 3, 4, 5, Goodbye
Summary
In this lesson, the key points we should remember are:
- Loops: Loops allow for repeating a block of code multiple times based on certain conditions.
- While Loops: The
while
loop executes as long as its condition remains true - Do While Loops: The
do while
loop executes at least once, regardless of the initial condition. - For Loops: The
for
loop allows us to set initialization, condition, and update components. - Infinite Loops: Infinite loops do not end under normal circumstances. They're usually bugs, often caused by the loop body not affecting the boolean expression that determines when the loop should terminate
- Loop Scopes: Each iteration of a loop creates a new block scope. Variables defined within a loop are not accessible outside of it.
- Nesting Conditionals and Loops: We can use conditional statements and other loops within a loop, enhancing the flexibility of each.
- Control Statements in Loops: We can use
continue
,break
, andreturn
statements within loop bodies to further control the flow of our program
The Modulus Operator (%
)
Learn how we can use the modulus operator to get the remainder of integer division, and some common use cases.