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.
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:
if
statement will execute once if the provided expression is truewhile
statement will execute continuously, as long as the provided expression remains true.The phrase iteration is commonly used to describe code that involves loops. For example,
Log()
function"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!";
}
while
LoopsWhat will be the value of i
after running this code?
int i { 0 };
while (i <= 5) {
++i;
}
In most software we use, we’ve probably noted that the program runs continuously until we explicitly tell it to stop by, for example, pressing a button in the UI.
This is possible because the program is effectively just one big loop - usually a while
loop.
The outermost loop of a program, that keeps everything running, is sometimes referred to as the application loop or, if our application is specifically a game, the game loop. On every iteration, this loop orchestrates tasks like:
int main(){
bool shouldQuit{false};
while (!shouldQuit) {
shouldQuit = getUserInput();
UpdateState();
RenderToScreen();
}
}
We’ll be building our own, real application loops later in the course
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);
do while
LoopsWhat will be the value of i
after running this code?
int i = 0;
do {
++i;
} while (i < 0);
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:
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,
for
LoopsWhat 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.
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.
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.
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.
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,
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.
continue
StatementWe 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,
continue
StatementWhat 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++;
};
break
StatementThe 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.
break
StatementWhat will be the value of Number
after running this code?
int i { 0 };
while (i >= 0) {
if (i == 5) break;
++i;
};
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 functionThis 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
In this lesson, the key points we should remember are:
while
loop executes as long as its condition remains truedo while
loop executes at least once, regardless of the initial condition.for
loop allows us to set initialization, condition, and update components.continue
, break
, and return
statements within loop bodies to further control the flow of our programThe next lesson will delve into the modulus operator %
, a tool often used in conjunction with loops.
Learn how we can use loops to iterate over a block of code, executing it as many times as needed.
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way