1.4. Flow of Control
Statements normally execute sequentially: The first statement in a block is executed first, followed by the second, and so on. Of course, few programs—including the one to solve our bookstore problem—can be written using only sequential execution. Instead, programming languages provide various flow-of-control statements that allow for more complicated execution paths.
1.4.1. The while
Statement
A while
statement repeatedly executes a section of code so long as a given condition is true. We can use a while
to write a program to sum the numbers from 1 through 10 inclusive as follows:
#include <iostream>
int main()
{
int sum = 0, val = 1;
// keep executing the while as long as val is less than or equal to 10
while (val <= 10) {
sum += val; // assigns sum + val to sum
++val; // add 1 to val
}
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
return 0;
}
When we compile and execute this program, it prints
Sum of 1 to 10 inclusive is 55
As before, we start by including the iostream
header and defining main
. Inside main
we define two int
variables: sum
, which will hold our summation, and val
, which will represent each of the values from 1 through 10. We give sum
an initial value of 0
and start val
off with the value 1
.
The new part of this program is the while
statement. A while
has the form
while (condition)
statement
A while
executes by (alternately) testing the condition and executing the associated statement until the condition is false. A condition is an expression that yields a result that is either true or false. So long as condition is true, statement is executed. After executing statement, condition is tested again. If condition is again true, then statement is again executed. The while
continues, alternately testing the condition and executing statement until the condition is false.
In this program, the while
statement is
// keep executing the while as long as val is less than or equal to 10
while (val <= 10) {
sum += val; // assigns sum + val to sum
++val; // add 1 to val
}
The condition uses the less-than-or-equal operator (the <=
operator) to compare the current value of val
and 10
. As long as val
is less than or equal to 10, the condition is true. If the condition is true, we execute the body of the while
. In this case, that body is a block with two statements:
{
sum += val; // assigns sum + val to sum
++val; // add 1 to val
}
A block is a sequence of zero or more statements enclosed by curly braces. A block is a statement and may be used wherever a statement is required. The first statement in this block uses the compound assignment operator (the +=
operator). This operator adds its right-hand operand to its left-hand operand and stores the result in the left-hand operand. It has essentially the same effect as writing an addition and an assignment:
sum = sum + val; // assign sum + val to sum
Thus, the first statement in the block adds the value of val
to the current value of sum
and stores the result back into sum
.
The next statement
++val; // add 1 to val
uses the prefix increment operator (the ++
operator). The increment operator adds 1
to its operand. Writing ++val
is the same as writing val = val + 1
.
After executing the while
body, the loop evaluates the condition again. If the (now incremented) value of val
is still less than or equal to 10, then the body of the while
is executed again. The loop continues, testing the condition and executing the body, until val
is no longer less than or equal to 10.
Once val
is greater than 10, the program falls out of the while
loop and continues execution with the statement following the while
. In this case, that statement prints our output, followed by the return
, which completes our main
program.
INFO
Exercises Section 1.4.1
Exercise 1.9: Write a program that uses a while
to sum the numbers from 50 to 100.
Exercise 1.10: In addition to the ++
operator that adds 1
to its operand, there is a decrement operator (--
) that subtracts 1
. Use the decrement operator to write a while
that prints the numbers from ten down to zero.
Exercise 1.11: Write a program that prompts the user for two integers. Print each number in the range specified by those two integers.
1.4.2. The for
Statement
In our while
loop we used the variable val
to control how many times we executed the loop. We tested the value of val
in the condition and incremented val
in the while
body.
This pattern—using a variable in a condition and incrementing that variable in the body—happens so often that the language defines a second statement, the for
statement, that abbreviates code that follows this pattern. We can rewrite this program using a for
loop to sum the numbers from 1 through 10 as follows:
#include <iostream>
int main()
{
int sum = 0;
// sum values from 1 through 10 inclusive
for (int val = 1; val <= 10; ++val)
sum += val; // equivalent to sum = sum + val
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
return 0;
}
As before, we define sum
and initialize it to zero. In this version, we define val
as part of the for
statement itself:
for (int val = 1; val <= 10; ++val)
sum += val;
Each for
statement has two parts: a header and a body. The header controls how often the body is executed. The header itself consists of three parts: an init-statement, a condition, and an expression. In this case, the init-statement
int val = 1;
defines an int
object named val
and gives it an initial value of 1
. The variable val
exists only inside the for
; it is not possible to use val
after this loop terminates. The init-statement is executed only once, on entry to the for
. The condition
val <= 10
compares the current value in val
to 10
. The condition is tested each time through the loop. As long as val
is less than or equal to 10
, we execute the for
body. The expression is executed after the for
body. Here, the expression
++val
uses the prefix increment operator, which adds 1
to the value of val
. After executing the expression, the for
retests the condition. If the new value of val
is still less than or equal to 10
, then the for
loop body is executed again. After executing the body, val
is incremented again. The loop continues until the condition fails.
In this loop, the for
body performs the summation
sum += val; // equivalent to sum = sum + val
To recap, the overall execution flow of this for
is:
- Create
val
and initialize it to1
. - Test whether
val
is less than or equal to10
. If the test succeeds, execute thefor
body. If the test fails, exit the loop and continue execution with the first statement following thefor
body. - Increment
val
. - Repeat the test in step 2, continuing with the remaining steps as long as the condition is true.
INFO
Exercises Section 1.4.2
Exercise 1.12: What does the following for
loop do? What is the final value of sum
?
int sum = 0;
for (int i = -100; i <= 100; ++i)
sum += i;
Exercise 1.13: Rewrite the exercises from § 1.4.1 (p. 13) using for
loops.
Exercise 1.14: Compare and contrast the loops that used a for
with those using a while
. Are there advantages or disadvantages to using either form?
Exercise 1.15: Write programs that contain the common errors discussed in the box on page 16. Familiarize yourself with the messages the compiler generates.
1.4.3. Reading an Unknown Number of Inputs
In the preceding sections, we wrote programs that summed the numbers from 1 through 10. A logical extension of this program would be to ask the user to input a set of numbers to sum. In this case, we won’t know how many numbers to add. Instead, we’ll keep reading numbers until there are no more numbers to read:
#include <iostream>
int main()
{
int sum = 0, value = 0;
// read until end-of-file, calculating a running total of all values read
while (std::cin >> value)
sum += value; // equivalent to sum = sum + value
std::cout << "Sum is: " << sum << std::endl;
return 0;
}
If we give this program the input
3 4 5 6
then our output will be
Sum is: 18
The first line inside main
defines two int
variables, named sum
and value
, which we initialize to 0
. We’ll use value
to hold each number as we read it from the input. We read the data inside the condition of the while
:
while (std::cin >> value)
Evaluating the while
condition executes the expression
std::cin >> value
That expression reads the next number from the standard input and stores that number in value
. The input operator (§ 1.2, p. 8) returns its left operand, which in this case is std::cin
. This condition, therefore, tests std::cin
.
When we use an istream
as a condition, the effect is to test the state of the stream. If the stream is valid—that is, if the stream hasn’t encountered an error—then the test succeeds. An istream
becomes invalid when we hit end-of-file or encounter an invalid input, such as reading a value that is not an integer. An istream
that is in an invalid state will cause the condition to yield false.
Thus, our while
executes until we encounter end-of-file (or an input error). The while
body uses the compound assignment operator to add the current value to the evolving sum
. Once the condition fails, the while
ends. We fall through and execute the next statement, which prints the sum
followed by endl
.
INFO
Entering an End-of-File from the Keyboard
When we enter input to a program from the keyboard, different operating systems use different conventions to allow us to indicate end-of-file. On Windows systems we enter an end-of-file by typing a control-z—hold down the Ctrl key and press z
—followed by hitting either the Enter or Return key. On UNIX systems, including on Mac OS X machines, end-of-file is usually control-d.
INFO
Compilation Revisited
Part of the compiler’s job is to look for errors in the program text. A compiler cannot detect whether a program does what its author intends, but it can detect errors in the form of the program. The following are the most common kinds of errors a compiler will detect.
Syntax errors: The programmer has made a grammatical error in the C++ language. The following program illustrates common syntax errors; each comment describes the error on the following line:
// error: missing ) in parameter list for main
int main ( {
// error: used colon, not a semicolon, after endl
std::cout << "Read each file." << std::endl:
// error: missing quotes around string literal
std::cout << Update master. << std::endl;
// error: second output operator is missing
std::cout << "Write new master." std::endl;
// error: missing ; on return statement
return 0
}
Type errors: Each item of data in C++ has an associated type. The value 10, for example, has a type of int
(or, more colloquially, “is an int
”). The word "hello"
, including the double quotation marks, is a string literal. One example of a type error is passing a string literal to a function that expects an int
argument.
Declaration errors: Every name used in a C++ program must be declared before it is used. Failure to declare a name usually results in an error message. The two most common declaration errors are forgetting to use std::
for a name from the library and misspelling the name of an identifier:
#include <iostream>
int main()
{
int v1 = 0, v2 = 0;
std::cin >> v >> v2; // error: uses "v" not "v1"
// error: cout not defined; should be std::cout
cout << v1 + v2 << std::endl;
return 0;
}
Error messages usually contain a line number and a brief description of what the compiler believes we have done wrong. It is a good practice to correct errors in the sequence they are reported. Often a single error can have a cascading effect and cause a compiler to report more errors than actually are present. It is also a good idea to recompile the code after each fix—or after making at most a small number of obvious fixes. This cycle is known as edit-compile-debug.
INFO
Exercises Section 1.4.3
Exercise 1.16: Write your own version of a program that prints the sum of a set of integers read from cin
.
1.4.4. The if
Statement
Like most languages, C++ provides an if
statement that supports conditional execution. We can use an if
to write a program to count how many consecutive times each distinct value appears in the input:
#include <iostream>
int main()
{
// currVal is the number we're counting; we'll read new values into val
int currVal = 0, val = 0;
// read first number and ensure that we have data to process
if (std::cin >> currVal) {
int cnt = 1; // store the count for the current value we're processing
while (std::cin >> val) { // read the remaining numbers
if (val == currVal) // if the values are the same
++cnt; // add 1 to cnt
else { // otherwise, print the count for the previous value
std::cout << currVal << " occurs "
<< cnt << " times" << std::endl;
currVal = val; // remember the new value
cnt = 1; // reset the counter
}
} // while loop ends here
// remember to print the count for the last value in the file
std::cout << currVal << " occurs "
<< cnt << " times" << std::endl;
} // outermost if statement ends here
return 0;
}
If we give this program the following input:
42 42 42 42 42 55 55 62 100 100 100
then the output should be
42 occurs 5 times
55 occurs 2 times
62 occurs 1 times
100 occurs 3 times
Much of the code in this program should be familiar from our earlier programs. We start by defining val
and currVal
: currVal
will keep track of which number we are counting; val
will hold each number as we read it from the input. What’s new are the two if
statements. The first if
if (std::cin >> currVal) {
// ...
} // outermost if statement ends here
ensures that the input is not empty. Like a while
, an if
evaluates a condition. The condition in the first if
reads a value into currVal
. If the read succeeds, then the condition is true and we execute the block that starts with the open curly following the condition. That block ends with the close curly just before the return
statement.
Once we know there are numbers to count, we define cnt
, which will count how often each distinct number occurs. We use a while
loop similar to the one in the previous section to (repeatedly) read numbers from the standard input.
The body of the while
is a block that contains the second if
statement:
if (val == currVal) // if the values are the same
++cnt; // add 1 to cnt
else { // otherwise, print the count for the previous value
std::cout << currVal << " occurs "
<< cnt << " times" << std::endl;
currVal = val; // remember the new value
cnt = 1; // reset the counter
}
The condition in this if
uses the equality operator (the ==
operator) to test whether val
is equal to currVal
. If so, we execute the statement that immediately follows the condition. That statement increments cnt
, indicating that we have seen currVal
once more.
If the condition is false—that is, if val
is not equal to currVal
—then we execute the statement following the else
. This statement is a block consisting of an output statement and two assignments. The output statement prints the count for the value we just finished processing. The assignments reset cnt
to 1
and currVal
to val
, which is the number we just read.
WARNING
C++ uses =
for assignment and ==
for equality. Both operators can appear inside a condition. It is a common mistake to write =
when you mean ==
inside a condition.
INFO
Exercises Section 1.4.4
Exercise 1.17: What happens in the program presented in this section if the input values are all equal? What if there are no duplicated values?
Exercise 1.18: Compile and run the program from this section giving it only equal values as input. Run it again giving it values in which no number is repeated.
Exercise 1.19: Revise the program you wrote for the exercises in § 1.4.1 (p. 13) that printed a range of numbers so that it handles input in which the first number is smaller than the second.
INFO
Key Concept: Indentation and Formatting of C++ Programs
C++ programs are largely free-format, meaning that where we put curly braces, indentation, comments, and newlines usually has no effect on what our programs mean. For example, the curly brace that denotes the beginning of the body of main
could be on the same line as main
; positioned as we have done, at the beginning of the next line; or placed anywhere else we’d like. The only requirement is that the open curly must be the first nonblank, noncomment character following main
’s parameter list.
Although we are largely free to format programs as we wish, the choices we make affect the readability of our programs. We could, for example, have written main
on a single long line. Such a definition, although legal, would be hard to read.
Endless debates occur as to the right way to format C or C++ programs. Our belief is that there is no single correct style but that there is value in consistency. Most programmers indent subsidiary parts of their programs, as we’ve done with the statements inside main
and the bodies of our loops. We tend to put the curly braces that delimit functions on their own lines. We also indent compound IO expressions so that the operators line up. Other indentation conventions will become clear as our programs become more sophisticated.
The important thing to keep in mind is that other ways to format programs are possible. When you choose a formatting style, think about how it affects readability and comprehension. Once you’ve chosen a style, use it consistently.