In This Chapter
Controlling the flow through the program
Executing a group of statements repetitively
Avoiding infinite loops
The simple programs that appear in Chapters 1 through 4 process a fixed number of inputs, output the result of that calculation, and quit. However, these programs lack any form of flow control. They cannot make tests of any sort. Computer programs are all about making decisions. If the user presses a key, the computer responds to the command.
For example, if the user presses Ctrl+C, the computer copies the currently selected area to the Clipboard. If the user moves the mouse, the pointer moves on the screen. If the user clicks the right mouse button with the Windows key depressed, the computer crashes. The list goes on and on. Programs that don't make decisions are necessarily pretty boring.
Flow-control commands allow the program to decide what action to take based on the results of the C++ logical operations performed (see Chapter 4). There are basically three types of flow-control statements: the branch, the loop, and the switch.
The simplest form of flow control is the branch statement. This instruction allows the program to decide which of two paths to take through C++ instructions, based on the results of a logical expression (see Chapter 4 for a description of logical expressions).
In C++, the branch statement is implemented using the if
statement:
if (m > n) { // Path 1 // ...instructions to be executed if // m is greater than n } else { // Path 2 // ...instructions to be executed if not }
First, the logical expression m > n
is evaluated. If the result of the expression is true
, control passes down the path marked Path 1
in the previous snippet. If the expression is false
, control passes to Path 2
. The else
clause is optional. If it is not present, C++ acts as if it is present but empty.
Actually, the braces are optional if there's only one statement to execute as part of the if
. If you lose the braces, however, it's embarrassingly easy to make a mistake that the C++ compiler can't catch. The braces serve as a guide marker; it's much safer to include 'em.
The following program demonstrates the if
statement (note all the lovely braces):
// BranchDemo - input two numbers. Go down one path of the // program if the first argument is greater // than the first or the other path if not #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // input the first argument... int nArg1; cout << "Enter arg1: "; cin >> nArg1; // ...and the second int nArg2; cout << "Enter arg2: "; cin >> nArg2; // now decide what to do: if (nArg1 > nArg2)
{ cout<< "Argument 1 is greater than argument 2" << endl; } else { cout<< "Argument 1 is not greater than argument 2" << endl; } // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
Here the program reads two integers from the keyboard and compares them. If nArg1
is greater than nArg2
, control flows to the output statement cout << "Argument 1 is greater than argument 2"
. If nArg1
is not greater than nArg2
, control flows to the else
clause where the statement cout << "Argument 1 is not greater than argument 2
"
is executed. Here's what that operation looks like:
Enter arg1: 5 Enter arg2: 6 Argument 1 is not greater than argument 2 Press any key to continue ...
Notice how the instructions within the if
blocks are indented slightly. This is strictly for human consumption because C++ ignores whitespace (spaces, tabs, and newlines). It may seem trivial but a clear coding style increases the readability of your C++ program. The Code::Blocks editor can enforce this or any one of several other coding guidelines for you.
Branch statements allow you to control the flow of a program's execution from one path of a program or another. This is a big improvement but still not enough to write full-strength programs.
Consider the problem of updating the computer display. The typical PC must update well over a thousand pixels as it paints an image from left to right. It repeats this process for each of the thousand or so rows on the display. A program would require several million instructions just to display a single image if the program did not loop through the same set of instructions for each pixel.
The simplest form of looping statement is the while
loop. Here's what the while
loop looks like:
while(condition) { // ...repeatedly executed as long as condition is true }
The condition is tested. This condition could be if var > 10
or if var1 == var2
or anything else you might think of. If the condition is true
, the statements within the braces are executed. Upon encountering the closed brace, C++ returns control to the beginning, and the process starts over. The effect is that the C++ code within the braces is executed repeatedly as long as the condition is true
. (Kind of reminds me of how I get to walk around the yard with my dog until she ... well, until we're done.)
If the condition were true
the first time, what would make it be false
in the future? Consider the following example program:
// WhileDemo - input a loop count. Loop while // outputting astring arg number of times. #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // input the loop count int nLoopCount; cout << "Enter loop count: "; cin >> nLoopCount; // now loop that many times while (nLoopCount > 0) { nLoopCount = nLoopCount - 1; cout << "Only " << nLoopCount << " loops to go" << endl; } // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
WhileDemo
begins by retrieving a loop count from the user, which it stores in the variable nLoopCount
. The program then executes a while
loop. The while
first tests nLoopCount
. If nLoopCount
is greater than 0, the program enters the body of the loop (the body is the code between the braces), where it decrements nLoopCount
by 1 and outputs the result to the display. The program then returns to the top of the loop to test whether nLoopCount
is still positive.
When executed, the program WhileDemo
outputs the results shown in this next snippet. Here I entered a loop count of 5
. The result is that the program loops five times, each time outputting a countdown:
Enter loop count: 5 Only 4 loops to go Only 3 loops to go Only 2 loops to go Only 1 loops to go Only 0 loops to go Press any key to continue ...
If the user enters a negative loop count, the program skips the loop entirely. That's because the specified condition is never true
, so control never enters the loop. In addition, if the user enters a very large number, the program loops for a long time before completing.
A separate, less frequently used version of the while
loop known as the do ... while
appears identical except the condition isn't tested until the bottom of the loop:
do { // ...the inside of the loop } while (condition);
Because the condition isn't tested until the end, the body of the do ... while
is always executed at least once.
The condition is checked only at the beginning of the while
loop or at the end of the do ... while
loop. Even if the condition ceases to be true
at some time during the execution of the loop, control does not exit the loop until the condition is retested.
Programmers very often use the autoincrement ++ or the autodecrement − − operators with loops that count something. Notice from the following snippet extracted from the WhileDemo
example that the program decrements the loop count by using assignment and subtraction statements, like this:
// now loop that many times while (nLoopCount > 0) { nLoopCount = nLoopCount - 1; cout << "Only " << loopCount << " loops to go" << endl; }
A more compact version uses the autodecrement feature, which does what you may well imagine:
while (nLoopCount > 0) { nLoopCount--; cout << "Only " << nLoopCount << " loops to go" << endl; }
The logic in this version is the same as in the original. The only difference is the way that nLoopCount
is decremented.
Because the autodecrement both decrements its argument and returns its value, the decrement operation can be combined with the while
loop. In particular, the following version is the smallest loop yet:
while (nLoopCount-- > 0) { cout << "Only " << nLoopCount << " loops to go" << endl; }
Believe it or not, nLoopcount-- > 0
is the version that most C++ programmers would use. It's not that C++ programmers like being cute (although they do). In fact, the more compact version (which embeds the autoincrement or autodecrement feature in the logical comparison) is easier to read, especially as you gain experience.
Both nLoopCount--
and --nLoopCount
expressions decrement nLoopCount
. The former expression, however, returns the value of loopCount
before being decremented; the latter expression does so after being decremented.
How often should the autodecrement version of WhileDemo
execute when the user enters a loop count of 1? If you use the pre-decrement version, the value of --nLoopCount
is 0, and the body of the loop is never entered. With the post-decrement version, the value of loopCount--
is 1, and control enters the loop.
Beware thinking that the version of the program with the autodecrement command executes faster (since it contains fewer statements). It probably executes exactly the same. Modern compilers are good at getting the number of machine-language instructions down to a minimum, no matter which of the decrement instructions shown here you actually use.
The most common form of loop is the for
loop. The for
loop is preferred over the more basic while
loop because it's generally easier to read (there's really no other advantage).
The for
loop has the following format:
for (initialization; conditional; increment) { // ...body of the loop }
The for
loop is equivalent to the following while
loop:
{ initialization; while(conditional) { { // ...body of the loop } increment; } }
Execution of the for
loop begins with the initialization clause, which got its name because it's normally where counting variables are initialized. The initialization clause is executed only once, when the for
loop is first encountered.
Execution continues with the conditional clause. This clause works just like the while
loop: As long as the conditional clause is true
, the for
loop continues to execute.
After the code in the body of the loop finishes executing, control passes to the increment clause before returning to check the conditional clause – thereby repeating the process. The increment clause normally houses the autoincrement or autodecrement statements used to update the counting variables.
The for
loop is best understood by example. The following ForDemo
program is nothing more than the WhileDemo
converted to use the for
loop construct:
// ForDemo1 - input a loop count. Loop while // outputting astring arg number of times. #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // input the loop count int nLoopCount; cout << "Enter loop count: "; cin >> nLoopCount; // count up to the loop count limit for (; nLoopCount > 0;) { nLoopCount = nLoopCount - 1; cout << "Only " << nLoopCount << " loops to go" << endl; } // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
The program reads a value from the keyboard into the variable loopCount
. The for
starts out comparing loopCount
to 0. Control passes into the for
loop if loopCount
is greater than 0. Once inside the for
loop, the program decrements loopCount
and displays the result. That done, the program returns to the for
loop control. Control skips to the next line after the for
loop as soon as loopCount
has been decremented to 0.
All three sections of a for
loop may be empty. An empty initialization or increment section does nothing. An empty comparison section is treated like a comparison that returns true
.
This for
loop has two small problems. First, it's destructive — not in the sense of what my puppy does to a slipper, but in the sense that it changes the value of loopCount
, "destroying" the original value. Second, this for
loop counts backward from large values down to smaller values. These two problems are addressed by adding a dedicated counting variable to the for
loop. Here's what it looks like:
// ForDemo2 - input a loop count. Loop while // outputting astring arg number of times. #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // input the loop count int nLoopCount; cout << "Enter loop count: "; cin >> nLoopCount; // count up to the loop count limit for (int i = 1; i <= nLoopCount; i++) { cout << "We've finished " << i << " loops" << endl; } // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
This modified version of ForDemo
loops the same as it did before. Instead of modifying the value of nLoopCount
, however, this ForDemo2
version uses a new counter variable.
This for
loop declares a counter variable i
and initializes it to 0. It then compares this counter variable to nLoopCount
. If i
is less than nLoopCount
, control passes to the output statement within the body of the for
loop. Once the body has completed executing, control passes to the increment clause where i
is incremented and compared to nLoopCount
again and so it goes.
The following shows example output from the program:
Enter loop count: 5 We've finished 1 loops We've finished 2 loops We've finished 3 loops We've finished 4 loops We've finished 5 loops Press any key to continue ...
When declared within the initialization portion of the for
loop, the index variable is known only within the for
loop itself. Nerdy C++ programmers say that the scope of the variable is the for
loop. In the ForDemo2
example just given, the variable i
is not accessible from the return
statement because that statement is not within the loop.
An infinite loop is an execution path that continues forever. An infinite loop occurs any time the condition that would otherwise terminate the loop can't occur — usually the result of a coding error.
Consider the following minor variation of the earlier loop:
while (nLoopCount > 0) { cout << "Only " << nLoopCount << " loops to go" << endl; }
The programmer forgot to decrement the variable nLoopCount
. The result is a loop counter that never changes. The test condition is either always false
or always true
. The program executes in a never-ending (infinite) loop.
I realize that nothing's infinite. Eventually the power will fail, the computer will break, Microsoft will go bankrupt, and dogs will sleep with cats.... Either the loop will stop executing, or you won't care anymore. But an infinite loop will continue to execute until something outside the control of the program makes it stop.
You can create an infinite loop in many more ways than shown here, most of which are a lot more difficult to spot than this was.
C++ defines two special flow-control commands known as break
and continue
. Sometimes the condition for terminating a loop occurs at neither the beginning nor the end of the loop, but in the middle. Consider a program that accumulates numbers of values entered by the user. The loop terminates when the user enters a negative number.
The challenge with this problem is that the program can't exit the loop until the user has entered a value but must exit before the value is added to the sum.
For these cases, C++ defines the break
command. When encountered, the break
causes control to exit the current loop immediately. Control passes from the break
statement to the statement immediately following the closed brace at the end of the loop.
The format of the break
commands is as follows:
while(condition) // break works equally well in for loop { if (some other condition) { break; // exit the loop } } // control passes here when the // program encounters the break
Armed with this new break
command, my solution to the accumulator problem appears as the program BreakDemo
:
// BreakDemo - input a series of numbers. // Continue to accumulate the sum // of these numbers until the user // enters a negative number. #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // input the loop count int accumulator = 0; cout << "This program sums values entered" << "by the user "; cout << "Terminate the loop by entering " << "a negative number" << endl;
// loop "forever" for(;;) { // fetch another number int nValue = 0; cout << "Enter next number: "; cin >> nValue; // if it's negative... if (nValue < 0) { // ...then exit break; } // ...otherwise add the number to the accumulator accumulator += nValue; } // now that we've exited the loop // output the accumulated result cout << " The total is " << accumulator << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
After explaining the rules to the user (entering a negative number to terminate and so on), the program enters what looks like an infinite for
loop. Once within the loop, BreakDemo
retrieves a number from the keyboard. Only after the program has read the number can it test to see whether that number matches the exit criteria. If the input number is negative, control passes to the break
, causing the program to exit the loop. If the input number is not negative, control skips over the break
command to the expression that sums the new value into the accumulator. After the program exits the loop, it outputs the accumulated value and then exits.
When performing an operation on a variable repeatedly in a loop, make sure that the variable is initialized properly before entering the loop. In this case, the program zeros accumulator
before entering the loop where nValue
is added to it.
The result of an example run appears as follows:
This program sums values entered by the user Terminate the loop by entering a negative number Enter next number: 1 Enter next number: 2 Enter next number: 3 Enter next number: −1 The total is 6 Press any key to continue ...
The similar continue
command is used less frequently. When the program encounters the continue
command, it immediately moves back to the top of the loop. The rest of the statements in the loop are ignored for the current iteration.
The following example snippet ignores negative numbers that the user might input. Only a 0 terminates this version (the complete program appears on the CD-ROM as ContinueDemo
):
while(true)// this while() has the same effect as for(;;) { // input a value cout << "Input a value:"; cin >> nValue; // if the value is negative... if (nValue < 0) { // ...output an error message... cout << "Negative numbers are not allowed "; // ...and go back to the top of the loop continue; } // ...continue to process input like normal }
Return to our PC-screen-repaint problem. Surely it must need a loop structure of some type to write each pixel from left to right on a single line. (Do Middle Eastern terminals scan from right to left? I have no idea.) What about repeatedly repainting each scan line from top to bottom? (Do PC screens in Australia scan from bottom to top?) For this particular task, you need to include a left-to-right scan loop within the top-to-bottom scan loop.
A loop command within another loop is known as a nested loop. As an example, I have modified the BreakDemo
program to accumulate any number of sequences. In this NestedDemo
program, the inner loop sums numbers entered from the keyboard until the user enters a negative number. The outer loop continues accumulating sequences until the sum is 0. Here's what it looks like:
// NestedDemo - input a series of numbers. // Continue to accumulate the sum // of these numbers until the user // enters a 0. Repeat the process // until the sum is 0. #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; int main(int nNumberofArgs, char* pszArgs[]) { // the outer loop cout << "This program sums multiple series " << "of numbers. Terminate each sequence " << "by entering a negative number. " << "Terminate the series by entering two " << "negative numbers in a row "; // continue to accumulate sequences int accumulator; for(;;) { // start entering the next sequence // of numbers accumulator = 0; cout << "Start the next sequence "; // loop forever for(;;) { // fetch another number int nValue = 0; cout << "Enter next number: "; cin >> nValue; // if it's negative... if (nValue < 0) {
// ...then exit break; } // ...otherwise add the number to the // accumulator accumulator += nValue; } // exit the loop if the total accumulated is 0 if (accumulator == 0) { break; } // output the accumulated result and start over cout << "The total for this sequence is " << accumulator << endl << endl; } // we're about to quit cout << "Thank you" << endl; // wait until user is ready before terminating program // to allow the user to see the program results system("PAUSE"); return 0; }
Notice the inner for loop looks like the earlier accumulator example. Immediately after that loop, however, is an added test. If accumulator
is equal to 0, the program executes a break statement that exits the outer loop. Otherwise, the program outputs the accumulated value and starts over.
One last control statement is useful in a limited number of cases. The switch
statement resembles a compound if
statement by including a number of different possibilities rather than a single test:
switch(expression) { case c1: // go here if the expression == c1 break;
case c2: // go here if expression == c2 break; default: // go here if there is no match }
The value of expression must be an integer (int, long
, or char
). The case values must be constants. When the switch
statement is encountered, the expression is evaluated and compared to the various case constants. Control branches to the case that matches. If none of the cases matches, control passes to the default
clause.
Consider the following example code snippet:
int choice; cout << "Enter a 1, 2 or 3:"; cin >> choice; switch(choice) { case 1: // do "1" processing break; case 2: // do "2" processing break; case 3: // do "3" processing break; default: cout << "You didn't enter a 1, 2 or 3 "; }
Once again, the switch
statement has an equivalent, in this case multiple if
statements. However, when there are more than two or three cases, the switch
structure is easier to understand.
3.15.140.68