Overview
Teaching: 30 min
Exercises: 0 minQuestions
How do I control the flow of execution in C++?
Objectives
Learn about the different control structures.
Use decision and looping structures to see how they work.
Simple C++ statements are the individual instructions in a program, and always end with a semicolon. Simple statements are executed in the same order in which they appear in a program.
However, programs are not limited to a linear sequence of statements. During execution, a program may repeat segments of code, or make decisions to execute different sections of code. Like most other languages, C++ provides a number of flow control operations that enable the sequential flow of execution to be modified by the programmer.
In C++, flow control operations generally act on a block of code. This block may be a single C++ statement terminated by a semicolon, or a compound statement. A compound statement is a group of statements, each of them terminated by its own semicolon. The grouping is indicated by surrounding the statements with curly braces as follows:
{ statement1; statement2; statement3; }
C++ considers this entire block to be a single statement.
Selection statements are used to make decisions about how the flow of execution should proceed.
if
and else
statementsThe if
keyword is used to execute a statement or block, if, and only if, a condition is fulfilled. Its syntax is:
if (condition) statement
Here, condition
is an expression that is to be evaluated. If the result of the expression is true, statement
is executed. If it is false,
statement
is not executed and is simply ignored. The program continues right after the entire if
statement.
For example, the following code fragment prints the message “x is 100”, only if the value stored in the x
variable is indeed 100:
if (x == 100)
cout << "x is 100" << endl;
Remember that C++ allows any kind of spaces or indenting, so we’ve moved the statement to the following line and indented it to make it more readable.
If you want to include more than a single statement to be executed when the condition is true, these statements are enclosed in a block as follows:
if (x == 100)
{
cout << "x is ";
cout << x << endl;
}
As usual, indentation and line breaks in the code have no effect, so the above code is equivalent to:
if (x == 100) { cout << "x is "; cout << x; }
Selection statements with if
can also specify what happens when the condition is not fulfilled. This is achieved by using the else
keyword to
introduce an alternative statement. Its syntax is:
if (condition) statement1 else statement2
In this case, statement1
is executed when condition
is true, and statement2
is executed when it is false.
For example:
if (x == 100)
cout << "x is 100" << endl;
else
cout << "x is not 100" << endl;
This prints “x is 100”, if indeed x
has a value of 100. If x
has any other value, it prints “x is not 100” instead.
The else
keyword can also be combined with if
to check a range of conditions. For example:
if (x > 0)
cout << "x is positive" << endl;
else if (x < 0)
cout << "x is negative" << endl;
else
cout << "x is 0" << endl;
This prints “x is positive” if x
is greater than zero, “x is negative” if x
is less than zero, or “x is 0” if x
is exactly equal to zero.
Note that else if
are two separate keywords, not a single keyword, so the can be separated by spaces and newlines.
Abiguity
What do you think the following program will print out?
#include <iostream> using namespace std; int main() { int x = -1; if (x > 0) cout << "x is positive" << endl; cout << "have a great day" << endl; return 0; }
Now run the program and check what output is produced. Did it do what you expect?
Solution
When you run the program, you should see the following:
have a great day
Why is this? Recall that the
if
statement takes a single statement or a block of statements. The programmer has indented both statements thinking that they will both be executed if the condition is true, however as they are not enclosed in braces, only the first statement would be executed. The second statement is executed regardless of the value of the condition.Assuming that the the programmer intended both statements to be included in
if
, the correct code is:if (x > 0) { cout << "x is positive" << endl; cout << "have a great day" << endl; }
Loops repeat a statement a certain number of times, or while some condition is fulfilled. They are introduced by the keywords while
,
do
, and for
.
while
loopThe simplest kind of loop is the while
loop. Its syntax is:
while (expression) statement
The while
loop simply repeats statement while the expression
is true. If, after any execution of statement
, expression
is no longer
true, the loop ends, and the program continues right after the loop. For example, let’s have a look at a countdown using a while
loop:
// custom countdown using while
#include <iostream>
using namespace std;
int main()
{
int n = 10;
while (n > 0) {
cout << n << ", ";
--n;
}
cout << "liftoff!\n";
}
This code generates the following output:
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!
The first statement in the function main
sets n
to a value of 10. This is the initial number for the countdown. Then the while
loop begins.
If this value fulfills the condition n > 0
, then the block that follows the condition is executed, and repeated for as long as the
condition remains true. The body of the loop does two things. First it outputs the value of n
followed by a comma, then it
decrements the value of n
by one. The loop condition is now evaluated again, and because it is still true, the loop body will again
be executed. This will continue until the value of n
becomes 0, at which point the loop will finish, and the string liftoff!
will
be output.
An important thing to consider with while
loops is that the loop should end at some point, and thus the statement
part must alter
the value (or values) checked in the condition in some way. Otherwise, the loop will continue forever.
do-while
loopA very similar loop is the do-while
loop, whose syntax is:
do statement while (condition);
This loop behaves like a while
loop, except that condition
is evaluated after the execution of statement
instead of before. This
guarantees at least one execution of statement
, even if condition
is never fulfilled. For example, the following example program
echoes any text the user introduces until the user enters “goodbye”:
// echo machine
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
do {
cout << "Enter text: ";
getline(cin,str);
cout << "You entered: " << str << endl;
} while (str != "goodbye");
return 0;
}
Sample output would be:
Enter text: hello
You entered: hello
Enter text: who's there?
You entered: who's there?
Enter text: goodbye
You entered: goodbye
The do-while
loop is usually preferred over a while
loop when the statement needs to be executed at least once, such as when the
value of condition
is determined within statement
. In the last example, the user
input within the block is what will determine if the loop ends. Even if the user wants to end the loop as soon as possible
by entering “goodbye”, the block in the loop needs to be executed at least once to prompt for input, and the condition can
only be determined after the input has been entered.
for
loopThe for
loop is designed to iterate a specific number of times. Its syntax is:
for (initialize; condition; modify) statement;
Like the while
loop, the for
loop repeats statement
while condition
is true. However, in addition, the for
loop provides
the ability to initialize and modify values that affect the loop condition
. The initialize
section is executed before the loop begins the first time, and
the modify
section is executed immediately after each iteration, but before condition
is checked.
The for
loop is identical to the following while
loop:
initialize;
while (condition) { statement; modify; }
Here is the same countdown example using a for
loop instead:
// countdown using a for loop
#include <iostream>
using namespace std;
int main()
{
for (int n = 10; n > 0; n--) {
cout << n << ", ";
}
cout << "liftoff!" << endl;
}
The three fields in a for
loop are optional. They can be left empty, but the semicolon separators are still required.
For example, the following loops are valid:
for (; n < 10;) {}
for (; n < 10; ++n) {}
for (i = 0; ; i++) {}
The last example shows a loop with no condition. This is equivalent to a loop with true
as condition (i.e., an infinite loop).
Because each of the fields is executed in a particular time in the lifecycle of a loop, it may be useful to execute more than a single
expression as any of initialize
, condition
, or modify
. The syntax of C++ does not allow blocks to be used for these components,
however, it is possible to include multiple statements using the comma operator:
for ( n=0, i=100 ; n!=i ; ++n, --i )
{
// whatever here...
}
This loop will execute 50 times assuming n
and i
are not modified within the loop:
for
loopThe for
loop has another syntax, which is used exclusively with ranges:
for ( declaration : range ) statement;
This kind of for
loop iterates over all the elements in range
, where declaration
declares some variable able to take the value of an
element in this range. Ranges are sequences of elements, including arrays, containers, and any other type supporting the functions
begin
and end
. Most of these types have not yet been introduced in this tutorial, but we are already acquainted with at least one
kind of range: the string
class.
An example of range-based for loop using strings:
// range-based for loop
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str {"Hello!"};
for (char c : str)
{
cout << "[" << c << "]";
}
cout << endl;
}
This program generates the oputput:
[H][e][l][l][o][!]
Note that the declaration of a char
variable (the elements in a string
are of type char
) is included in the for
loop.
We can then use this variable, c
, in the statement block to represent the value of each of the elements in the range.
This loop is automatic and does not require the explicit declaration of any counter variable.
Challenge
Convert the
do-while
loop in the following program into the equivalentfor
loop (i.e. so they produce the same output). Run the program and test that you get the correct result.// skip counting #include <iostream> using namespace std; int main() { int n; n = -5; do { n += 3; cout << n << ", "; } while (n <= 7); cout << "n is finally " << n << endl; }
Sometimes it is desirable re-test the condition of a loop, or exit the loop completely before the condition becomes false. While it is possible
to do this with the judicious use of if
statements, C++ provides two additional keywords that make this easier.
continue
statementThe continue
statement causes the program to skip the rest of the loop in the current iteration, as if the end of the statement block had
been reached. The loop will immediately start the following iteration. In the case of while
and for
loops, the condition will be tested,
but in the case of for
loops, the modify
part will not be executed.
For example, let’s skip the number 5 in our countdown:
// continue loop example
#include <iostream>
using namespace std;
int main()
{
for (int n=10; n>0; n--) {
if (n==5) continue;
cout << n << ", ";
}
cout << "liftoff!" << endl;
}
In this case, we now see the output:
10, 9, 8, 7, 6, 4, 3, 2, 1, liftoff!
Notice that the number 5 is missing.
break
statementThe break
statement immediately leaves a loop, even if the condition
is not false. It can also be used to end an infinite
loop.
For example, let’s stop the countdown before it would normally end:
// break loop example
#include <iostream>
using namespace std;
int main()
{
for (int n=10; n>0; n--)
{
cout << n << ", ";
if (n==3)
{
cout << "countdown aborted!";
break;
}
}
cout << endl;
}
Here we see the output
10, 9, 8, 7, 6, 5, 4, 3, countdown aborted!
switch
statementThe syntax of the switch
statement is a bit peculiar. Its purpose is to check for a value among a number of possible constant expressions.
It is similar to concatenating if-else
statements, but limited to checking the values against constant expressions. Its most typical
syntax is:
switch (expression)
{
case constant1:
group-of-statements-1;
break;
case constant2:
group-of-statements-2;
break;
.
.
.
default:
default-group-of-statements
}
The switch
statement works in the following way. The expression
is first evaluated and tested to see if it is equivalent to constant1
.
If it is, the group-of-statements-1
are executed until a break
statement is encountered. At this point the program jumps to the end of the
entire switch
statement (following the closing brace).
If expression
was not equal to constant1
, it is then checked against constant2
. If it is equal, then group-of-statements-2
are executed until
a break
is encountered, at which point it jumps to the code following the switch
.
Comparison proceeds for each case
until one is matched, or if the value of expression
does not match any of the previously specified
constants (there may be any number of these), the program executes the statements included after the default:
label, if it exists
(since it is optional).
Both of the following code fragments have the same behavior, demonstrating the if-else
equivalent of a switch
statement:
switch example | if-else equivalent |
---|---|
|
|
The switch
statement has a somewhat peculiar syntax inherited from the early times of the first C compilers, because it uses labels
instead of blocks. In the most typical use (shown above), this means that break
statements are needed after each group of statements
for a particular label. If break
1 is not included, all statements following the case (including those under any other labels) are
also executed, until the end of the switch
block or a jump statement (such as break
) is reached.
If the example above lacked the break
statement after the first group for the first case
, the program would not jump
automatically to the end of the switch
block after printing x is 1
, and would instead continue executing the
statements in the second case
, printing x is 2
. It would then continue executing until a break
statement was
encountered, or the end of the switch
block reached. This makes unnecessary to enclose the statements for each case in
braces {}
, and can also be useful to execute the same group of statements for different possible values.
For example:
switch (x) {
case 1:
case 2:
case 3:
cout << "x is 1, 2 or 3" << endl;
break;
default:
cout << "x is not 1, 2 nor 3" << endl;
}
Notice that switch
is limited to compareing its evaluated expression against labels that are constant expressions. It is not possible
to use variables or ranges as labels, because they are not valid C++ constant expressions. If you wish to do this,
as series of if
and else if
statements would be a better approach.
goto
statementThe goto
statement is rarely used, but it allows an absolute jump to another point in the program. This unconditional jump ignores nesting
levels, and does not cause any automatic stack unwinding. Therefore, this feature has to be used with care, and preferably within the same block
of statements, especially in the presence of local variables.
The destination point of the goto
is identified by a label, which is then used as an argument for the goto
statement. A label is made
of a valid identifier followed by a colon.
The goto
statement is generally deemed a low-level feature, with no particular use cases in modern higher-level programming paradigms
generally used with C++. But, just as an example, here is a version of our countdown loop using goto
:
// goto loop example
#include <iostream>
using namespace std;
int main ()
{
int n=10;
mylabel:
cout << n << ", ";
n--;
if (n>0) goto mylabel;
cout << "liftoff!" << endl;
}
Key Points
Decisions are made using
if
andselect
statements.Looping is performed with
while
,do-while
, andfor
statements.