5.4. Iterative Statements
Iterative statements, commonly called loops, provide for repeated execution until a condition is true. The while
and for
statements test the condition before executing the body. The do while
executes the body and then tests its condition.
5.4.1. The while
Statement
FundamentalA while
statement repeatedly executes a target statement as long as a condition is true. Its syntactic form is
while (condition)
statement
In a while
, statement (which is often a block) is executed as long as condition evaluates as true
. condition may not be empty. If the first evaluation of condition yields false
, statement is not executed.
The condition can be an expression or an initialized variable declaration (§ 5.2, p. 174). Ordinarily, the condition itself or the loop body must do something to change the value of the expression. Otherwise, the loop might never terminate.
INFO
Variables defined in a while
condition or while
body are created and destroyed on each iteration.
Using a while
Loop
A while
loop is generally used when we want to iterate indefinitely, such as when we read input. A while
is also useful when we want access to the value of the loop control variable after the loop finishes. For example:
INFO
Code for Exercise 5.13
(a)unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
(b)unsigned index = some_value();
(c)unsigned evenCnt = 0, oddCnt = 0;
(d)unsigned ival=512, jval=1024, kval=4096;
vector<int> v;
int i;
// read until end-of-file or other input failure
while (cin >> i)
v.push_back(i);
// find the first negative element
auto beg = v.begin();
while (beg != v.end() && *beg >= 0)
++beg;
if (beg == v.end())
// we know that all elements in v are greater than or equal to zero
The first loop reads data from the standard input. We have no idea how many times this loop will execute. The condition fails when cin
reads invalid data, encounters some other input failure, or hits end-of-file. The second loop continues until we find a negative value. When the loop terminates, beg
is either equal to v.end()
, or it denotes an element in v
whose value is less than zero. We can use the state of beg
outside the while
to determine further processing.
INFO
Exercises Section 5.4.1
Exercise 5.14: Write a program to read string
s from standard input looking for duplicated words. The program should find places in the input where one word is followed immediately by itself. Keep track of the largest number of times a single repetition occurs and which word is repeated. Print the maximum number of duplicates, or else print a message saying that no word was repeated. For example, if the input is
how now now now brown cow cow
the output should indicate that the word now
occurred three times.
5.4.2. Traditional for
Statement
FundamentalThe syntactic form of the for
statement is:
for (init-statement condition; expression)
statement
The for
and the part inside the parentheses is often referred to as the for
header.
INFO
init-statement must be a declaration statement, an expression statement, or a null statement. Each of these statements ends with a semicolon, so the syntactic form can also be thought of as
for (initializer; condition; expression)
statement
In general, init-statement is used to initialize or assign a starting value that is modified over the course of the loop. condition serves as the loop control. As long as condition evaluates as true
, statement is executed. If the first evaluation of condition yields false
, statement is not executed. expression usually modifies the variable(s) initialized in init-statement and tested in condition. expression is evaluated after each iteration of the loop. As usual, statement can be either a single or a compound statement.
Execution Flow in a Traditional for
Loop
Given the following for
loop from § 3.2.3 (p. 94):
// process characters in s until we run out of characters or we hit a whitespace
for (decltype(s.size()) index = 0;
index != s.size() && !isspace(s[index]); ++index)
s[index] = toupper(s[index]); // capitalize the current character
the order of evaluation is as follows:
init-statement is executed once at the start of the loop. In this example,
index
is defined and initialized to zero.- Next, condition is evaluated. If
index
is not equal tos.size()
and the character ats[index]
is not whitespace, thefor
body is executed. Otherwise, the loop terminates. If the condition isfalse
on the first iteration, then thefor
body is not executed at all. - If the condition is
true
, thefor
body executes. In this case, thefor
body makes the character ats[index]
uppercase. - Finally, expression is evaluated. In this example,
index
is incremented by 1.
These four steps represent the first iteration of the for
loop. Step 1 is executed only once on entry to the loop. Steps 2, 3, and 4 are repeated until the condition evaluates as false
—that is, when we encounter a whitespace character in s
, or index
is greater than s.size()
.
INFO
It is worth remembering that the visibility of any object defined within the for
header is limited to the body of the for
loop. Thus, in this example, index
is inaccessible after the for
completes.
Multiple Definitions in the for
Header
As in any other declaration, init-statement can define several objects. However, init-statement may be only a single declaration statement. Therefore, all the variables must have the same base type (§ 2.3, p. 50). As one example, we might write a loop to duplicate the elements of a vector
on the end as follows:
// remember the size of v and stop when we get to the original last element
for (decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i)
v.push_back(v[i]);
In this loop we define both the index, i
, and the loop control, sz
, in init-statement.
Omitting Parts of the for
Header
A for
header can omit any (or all) of init-statement, condition, or expression.
We can use a null statement for init-statement when an initialization is unnecessary. For example, we might rewrite the loop that looked for the first negative number in a vector
so that it uses a for
:
auto beg = v.begin();
for ( /* null */; beg != v.end() && *beg >= 0; ++beg)
; // no work to do
Note that the semicolon is necessary to indicate the absence of init-statement—more precisely, the semicolon represents a null init-statement. In this loop, the for
body is also empty because all the work of the loop is done inside the for
condition and expression. The condition decides when it’s time to stop looking and the expression increments the iterator.
Omitting condition is equivalent to writing true
as the condition. Because the condition always evaluates as true
, the for
body must contain a statement that exits the loop. Otherwise the loop will execute indefinitely:
for (int i = 0; /* no condition */ ; ++i) {
// process i; code inside the loop must stop the iteration!
}
We can also omit expression from the for
header. In such loops, either the condition or the body must do something to advance the iteration. As an example, we’ll rewrite the while
loop that read input into a vector
of int
s:
vector<int> v;
for (int i; cin >> i; /* no expression */ )
v.push_back(i);
In this loop there is no need for an expression because the condition changes the value of i
. The condition tests the input stream so that the loop ends when we’ve read all the input or encounter an input error.
5.4.3. Range for
Statement
FundamentalThe new standard introduced a simpler for
statement that can be used to iterate through the elements of a container or other sequence. The syntactic form of the range for
statement is:
for (declaration : expression)
statement
expression must represent a sequence, such as a braced initializer list (§ 3.3.1, p. 98), an array (§ 3.5, p. 113), or an object of a type such as vector
or string
that has begin
and end
members that return iterators (§ 3.4, p. 106).
declaration defines a variable. It must be possible to convert each element of the sequence to the variable’s type (§ 4.11, p. 159). The easiest way to ensure that the
INFO
Exercises Section 5.4.2
Exercise 5.15: Explain each of the following loops. Correct any problems you detect.
(a)for (int ix = 0; ix != sz; ++ix) { /*
(b)int ix;
(c)for (int ix = 0; ix != sz; ++ix, ++ sz) { /*
. . . */ }
Exercise 5.16: The while
loop is particularly good at executing while some condition holds; for example, when we need to read values until end-of-file. The for
loop is generally thought of as a step loop: An index steps through a range of values in a collection. Write an idiomatic use of each loop and then rewrite each using the other loop construct. If you could use only one loop, which would you choose? Why?
Exercise 5.17: Given two vector
s of int
s, write a program to determine whether one vector
is a prefix of the other. For vector
s of unequal length, compare the number of elements of the smaller vector
. For example, given the vector
s containing 0
, 1
, 1
, and 2
and 0
, 1
, 1
, 2
, 3
, 5
, 8
, respectively your program should return true
.
types match is to use the auto
type specifier (§ 2.5.2, p. 68). That way the compiler will deduce the type for us. If we want to write to the elements in the sequence, the loop variable must be a reference type.
On each iteration, the control variable is defined and initialized by the next value in the sequence, after which statement is executed. As usual, statement can be a single statement or a block. Execution ends once all the elements have been processed.
We have already seen several such loops, but for completeness, here is one that doubles the value of each element in a vector
:
vector<int> v = {0,1,2,3,4,5,6,7,8,9};
// range variable must be a reference so we can write to the elements
for (auto &r : v) // for each element in v
r *= 2; // double the value of each element in v
The for
header declares the loop control variable, r
, and associates it with v
. We use auto
to let the compiler infer the correct type for r
. Because we want to change the value of the elements in v
, we declare r
as a reference. When we assign to r
inside the loop, that assignment changes the element to which r
is bound.
A range for
is defined in terms of the equivalent traditional for
:
for (auto beg = v.begin(), end = v.end(); beg != end; ++beg) {
auto &r = *beg; // r must be a reference so we can change the element
r *= 2; // double the value of each element in v
}
Now that we know how a range for
works, we can understand why we said in § 3.3.2 (p. 101) that we cannot use a range for
to add elements to a vector
(or other container). In a range for
, the value of end()
is cached. If we add elements to (or remove them from) the sequence, the value of end
might be invalidated (§ 3.4.1, p. 110). We’ll have more to say about these matters in § 9.3.6 (p. 353).
5.4.4. The do while
Statement
A do while
statement is like a while
but the condition is tested after the statement body completes. Regardless of the value of the condition, we execute the loop at least once. The syntactic form is as follows:
do
statement
while (condition);
INFO
A do while
ends with a semicolon after the parenthesized condition.
In a do
, statement is executed before condition is evaluated. condition cannot be empty. If condition evaluates as false
, then the loop terminates; otherwise, the loop is repeated. Variables used in condition must be defined outside the body of the do while
statement.
We can write a program that (indefinitely) does sums using a do while
:
// repeatedly ask the user for a pair of numbers to sum
string rsp; // used in the condition; can't be defined inside the do
do {
cout << "please enter two values: ";
int val1 = 0, val2 = 0;
cin >> val1 >> val2;
cout << "The sum of " << val1 << " and " << val2
<< " = " << val1 + val2 << "\n\n"
<< "More? Enter yes or no: ";
cin >> rsp;
} while (!rsp.empty() && rsp[0] != 'n');
The loop starts by prompting the user for two numbers. It then prints their sum and asks whether the user wishes to do another sum. The condition checks that the user gave a response. If not, or if the input starts with an n
, the loop is exited. Otherwise the loop is repeated.
Because the condition is not evaluated until after the statement or block is executed, the do while
loop does not allow variable definitions inside the condition:
do {
// . . .
mumble(foo);
} while (int foo = get_foo()); // error: declaration in a do condition
If we could define variables in the condition, then any use of the variable would happen before the variable was defined!