C++: The Basics - eps1.14_Programflow - Loops
Hello and welcome. So far, we can only react to events and inputs and thus take different paths in the program flow. We do have intersections where the flow branches, but we can’t get back to a specific point in the source code.
However, we may want to come back to a fork again. Only this time we decide to go the other way.
Or we want an operation to be executed one more time, but with the current value.
So code that has already been executed is to be repeated. For this you have to program a loop.
goto
The most rudimentary return can be achieved with the keyword goto
. The name says it already. With this command you jump to the desired point in the code. However, this must be provided with a label, so that it is clearly recognizable.
The following example in listing6 shows a guessing game. A random number between 0
and 10
is formed and the user should guess with his input which number it is.
If his number answer was correct, a new random number is formed. Otherwise the old value remains.
After his attempt he is asked if he wants to guess one more time. No matter how successful he was. If the user wants to guess again and confirms this with y
, the instruction goto Start
jumps to the label Start
.
The label is before query for the number you are looking for. From here the source code is executed repeatedly. And this as often as the last query is answered with y
.
// listing1: goto instruction
#include <iostream>
#include <stdlib.h>
#include <string>
int main()
{
int guessable {rand() % 10};
int guess {0};
std::string retry {"y"};
std::cout << "Guess a number between 0 and 1 " << std::endl;
Start: // start label
std::cout << "Your guess: ";
std::cin >> guess;
if(guessable > guess)
{
std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
}
else if(guessable < guess)
{
std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
}
else
{
std::cout << "Great! "<< guess << " is right." << std::endl;
guessable = rand() % 10;
}
std::cout << "New try? (y/n)" << std::endl;
std::cin >> retry;
if(retry == "y")
{
goto Start; // goto instruction
}
else
{
std::cout << "See you!" << std::endl;
}
return 0;
}
Now that you’ve looked at the example and tried to follow it, I’ll come up with an ABER. goto is not the recommended type of program loops.
Extensive use of goto
commands can lead to unpredictable program flow. The program code is then no longer executed from top to bottom, but jumps back and forth in the lines. If you are not very careful, this can lead to unpredictable states of variables in some cases.
In the very worst case, programming with goto
leads to so-called spaghetti code. This means very sprawling, inefficient and confusing source code.
That’s why I prefer to use the other forms of loops, which we’ll look at next.
I just showed you goto so you know what it is in case you run across it in someone else’s source code.
while
With the keyword while you can initiate a loop similar to goto. You can’t jump to any point in your code with it. But this loop is very clear and gives you more security during execution.
while
is followed by a boolean expression in brackets ()
. This iterative statement determines whether the loop is executed or not. After the brackets you find the block for your statements.
As long as the evaluation of the Boolean expression results in true
, another loop pass of the statement block takes place. A loop pass is also called an iteration.
Let’s implement the example from listing1 with an while
loop instead of goto
.
// listing2: while loop
#include <iostream>
#include <stdlib.h>
#include <string>
int main()
{
int guessable {rand() % 10};
int guess {0};
std::string retry {"y"};
std::cout << "Guess a number between 0 and 1 " << std::endl;
while(retry == "y") // while loop
{
std::cout << "Your guess: ";
std::cin >> guess;
if(guessable > guess)
{
std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
}
else if(guessable < guess)
{
std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
}
else
{
std::cout << "Great! "<< guess << " is right." << std::endl;
guessable = rand() % 10;
}
std::cout << "New try? (y/n)" << std::endl;
std::cin >> retry;
}
std::cout << "See you!" << std::endl;
return 0;
}
The biggest difference to the goto
variant is the missing if
query at the end. Whether the user has entered y
and wants to try again does not have to be evaluated explicitly.
The condition retry =="y"
is the interative statement in this case. So the loop will only run until the variable is assigned a different value.
do while
You may want to run a code segment within a loop at least once. Completely independent of a condition. But further runs will only take place if the condition is fulfilled.
Here the do ... while
loop helps you. The statement block is preceded by the keyword do
. After the block the loop closes with the evaluation of the condition with while()
. This time while()
must be terminated with a semicolon ;
like any other statement.
In listing3 you can see how our example with do ... while
loop looks like.
// listing3: do while
#include <iostream>
#include <stdlib.h>
#include <string>
int main()
{
int guessable {rand() % 10};
int guess {0};
std::string retry {"n"};
std::cout << "Guess a number between 0 and 1 " << std::endl;
do // do
{
std::cout << "Your guess: ";
std::cin >> guess;
if(guessable > guess)
{
std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
}
else if(guessable < guess)
{
std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
}
else
{
std::cout << "Great! "<< guess << " is right." << std::endl;
guessable = rand() % 10;
}
std::cout << "New try? (y/n)" << std::endl;
std::cin >> retry;
}while(retry == "y"); // while loop
std::cout << "See you!" << std::endl;
return 0;
}
Since the variable retry
is only compared at the end of the loop, its value is irrelevant during initialization. The loop is run through in any case.
After the first run, the condition is evaluated. With true
the loop is run through again. Otherwise the following parts of the program are executed serially.
Infinite loop, continue and break
If you have an iterative statement that can never become false
, the loop will not find an end. It will never reach a termination condition. Such a construct with while(true)
is called Infinite Loop.
Sounds like a programming error at first. But it can be intentional and serve a purpose. Imagine you want to monitor a certain parameter during the whole runtime of the program. You can have the value constantly queried in a loop that never ends.
But the Infinite Loop is not uncontrollable.
We have already learned about the keyword break in connection with switch case. With it you can end a loop immediately. The program will continue after the loop.
Another keyword to control loops is continue
. If the program flow reaches this statement, it immediately jumps back to the beginning of the loop. All statements between continue
and the closing parenthesis are ignored.
Our example is implemented in listing4 with an infinite loop.
// listing4: infinite loop
#include <iostream>
#include <stdlib.h>
#include <string>
int main()
{
int guessable {rand() % 10};
int guess {0};
std::string retry {"y"};
std::cout << "Guess a number between 0 and 1 " << std::endl;
while(true) // infinite while loop
{
std::cout << "Your guess: ";
std::cin >> guess;
if(guessable > guess)
{
std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
continue;
std::cout << "You won't see me!" << std::endl;
}
else if(guessable < guess)
{
std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
continue;
std::cout << "You won't see me either!" << std::endl;
}
else
{
std::cout << "Great! "<< guess << " is right." << std::endl;
guessable = rand() % 10;
}
std::cout << "New try? (y/n)" << std::endl;
std::cin >> retry;
if(retry != "y")
break; // exit loop when expression evaluates to true
}
std::cout << "See you!" << std::endl;
return 0;
}
for
Next, let’s look at the for loop. It is a bit more complex than the previous loops because you can give it an initialization statement, a termination condition and an action after each pass. These are separated by a semicolon ‘;’ and are enclosed in brackets ‘()’ directly after the keyword ‘for’.
It is often used to modify an initialized variable until it reaches a certain value.
Thus, for example, you can define a counter variable with an initial value, check the value against an initial condition at the beginning of each loop, and modify the value of the variable at the end of a loop.
With a for
loop you can change our example so that the user has only a certain number of tries. If he does not manage to guess the correct number, he unfortunately loses.
// listing5: for loop
#include <iostream>
#include <stdlib.h>
int main()
{
int guessable {rand() % 10};
int guess {0};
int numberOfGuesses {3};
std::cout << "Guess a number between 0 and 10 " << std::endl;
for(int i = 0; i < numberOfGuesses; i++) // for loop
{
std::cout << numberOfGuesses - i << " guess left" << std::endl;
std::cout << "Your guess: ";
std::cin >> guess;
if(guessable > guess)
{
std::cout << "I'm afraid you missed! Your number is too small. " << std::endl;
}
else if(guessable < guess)
{
std::cout << "I'm afraid you missed! Your number is too large. " << std::endl;
}
else
{
std::cout << "Great! "<< guess << " is right." << std::endl;
break;
}
}
std::cout << "See you!" << std::endl;
return 0;
}
You can also initialize several variables in a for loop. But remember to separate them all with a comma ‘,’.
Using the initialization, the condition and the expression to be evaluated at the end of each loop pass is optional. You can also omit one, two or all.
If you omit all of them, the result is an infinte loop in the form of a for loop.
Nested Loops
Just like if
statements, you can also nest loops. This is especially useful when, for example, you have fields with multiple dimensions and want to apply an operation to each value.
In listing6, we take two one-dimensional arrays of different lengths and want to multiply each element of the first field by each of the second. To do this, we nest a loop that iterates one field inside another that is responsible for the other field.
// listing6: nested loop
#include <iostream>
int main()
{
const int array1Len = 3;
const int array2Len = 2;
int array1[array1Len] {26, -8, 0};
int array2[array2Len] {19, -5};
std::cout << "Multiplying each integer in array1 by each integer in array2:" << std::endl;
for(int i = 0; i < array1Len; ++i)
{
for(int j = 0; j < array2Len; ++j)
{
std::cout << array1[i] << " x " << array2[j] << " = " << array1[i] * array2[j] << std::endl;
}
}
return 0;
}
First all elements of array2
are multiplied by the first element from array1
. Then the whole process is repeated for the second element from array1
until every element has had a turn.
If you have a multidimensional array, you can use shaded loops to access each element. One loop goes through all columns, while the inner loop accesses each element of the row.
// listing7: multidimensional arrays
#include <iostream>
int main()
{
const int columns = 3;
const int rows = 2;
int arrayMulti [rows][columns] { {1,2,3},{4,5,6} };
for (int i = 0; i < rows; ++i)
{
// iterate integers in each row (columns)
for (int j = 0; j < columns; ++j)
{
std::cout << "Element[" << i << "][" << j << "] = " << arrayMulti[i][j] << std::endl;
}
}
return 0;
}
That sticks
In this post, we looked at the different types of loops. With their help, we can direct the program flow and write code more efficiently. If a particular statement is to be executed multiple times, we don’t need to type it each time. In a loop, the statement is repeated until the termination condition is met.
We have looked at the basic loop types while
, do while
and for
, as well as the special forms of Infinite Loops
and Nested Loops
.
In addition, we briefly looked at goto
. But just so you know what it is, in case you come across it in someone else’s source code. But with the other loop types every situation can be handled and they give you more security and a better overview of the program flow.
Now we have enough tools to design the flow of our programs.
I wish you maximum success!
Sources
- [1] B. Stroustrup, A Tour of C++. Pearson Education, 2. Auflage, 29. Juni 2018.
- [2] B. Stroustrup, Programming: Principles and Practice Using C++. Addison Wesley, 2. Auflage, 15. Mai 2014.
- [3] U. Breymann, Der C++ Programmierer. C++ lernen – professionell anwenden – Lösungen nutzen. Aktuell zu C++17. München: Carl Hanser Verlag GmbH & Co. KG; 5. Auflage, 6. November 2017