Chapter 8. More Control Statements

Grammar, which knows how to control even kings . . .

Molière

At this point we know enough to create very simple programs. In this chapter we move on to more complex statements. For controlled looping we have the for statement. We also study the switch statement. This statement can be quite powerful and complex, but as we shall see in the following sections, if used right it can be very efficient and effective.

But we start our discussion with a new looping statement, the for.

for Statement

The for statement allows you to execute a block of code a specified number of times. The general form of the for statement is:

for (initial-statement; condition; iteration-statement)
    body-statement;

This is equivalent to:

               initial-statement; 
while (condition) { 
    body-statement; 
    iteration-statement;
}

For example, Example 8-1 uses a while loop to add five numbers.

Example 8-1. total6/total6w.cpp
#include <iostream>

int total;      // total of all the numbers 
int current;    // current value from the user 
int counter;    // while loop counter 

int main(  ) {
    total = 0;

    counter = 0;
    while (counter < 5) {
        std::cout << "Number? ";

        std::cin >> current;
        total += current;

        ++counter;
    }
    std::cout << "The grand total is " << total << '
';
    return (0);
}

The same program can be rewritten using a for statement, as seen in Example 8-2.

Example 8-2. total6/total6.cpp
#include <iostream>

int total;      // total of all the numbers 
int current;    // current value from the user 
int counter;    // for loop counter 

int main(  ) {
    total = 0;
    for (counter = 0; counter < 5; ++counter) {
        std::cout << "Number? ";

        std::cin >> current;
        total += current;
    }
    std::cout << "The grand total is " << total << '
';
    return (0);
}

Note that counter goes from 0 to 4. Normally you count five items as 1, 2, 3, 4, 5. You will get along much better in C++ if you change your thinking to zero-based counting and count five items as 0, 1, 2, 3, 4. (One-based counting is one of the main causes of array overflow errors. See Chapter 5.)

Careful examination of the two flavors of this program reveals the similarities between the two versions, as shown in Figure 8-1.

Similarities between while and for
Figure 8-1. Similarities between while and for

Many older programming languages do not allow you to change the control variable (in this case counter) inside the loop. C++ is not so picky. You can change the control variable anytime you wish—you can jump into and out of the loop and generally do things that would make a PASCAL or FORTRAN programmer cringe. (Even though C++ gives you the freedom to do such insane things, that doesn’t mean you should do them.)

Question 8-1: What is the error in Example 8-3?

Example 8-3. cent/cent.cpp
#include <iostream>
/*
 * This program produces a Celsius to Fahrenheit conversion
 *    chart for the numbers 0 to 100.
 *
 * Restrictions:
 *      This program deals with integers only, so the
 *      calculations may not be exact.
 */

// the current Celsius temperature we are working with 
int celsius;

int main(  ) {
    for (celsius = 0; celsius <= 100; ++celsius);
        std::cout << "celsius: " << celsius << 
                " Fahrenheit: " << ((celsius * 9) / 5 + 32) << '
';
    return (0);
}

When run, this program prints out:

Celsius: 101 Fahrenheit: 213

and nothing more. Why?

Question 8-2: Example 8-4 reads a list of five numbers and counts the number of threes and sevens in the data. Why does it give us the wrong answers?

Example 8-4. seven/seven.cpp
#include <iostream>

int seven_count;    // number of sevens in the data
int data[5];        // the data to count 3 and 7 in 
int three_count;    // the number of threes in the data 
int the_index;      // index into the data 

int main(  ) {
    seven_count = 0;
    three_count = 0;

    std::cout << "Enter 5 numbers
";
    std::cin >> data[1] >> data[2] >> data[3] >>
           data[4] >> data[5];

    for (the_index = 1; the_index <= 5; ++the_index) {

        if (data[the_index] == 3)
            ++three_count;

        if (data[the_index] == 7)
            ++seven_count;
    }
    std::cout << "Threes " << three_count << 
        " Sevens " << seven_count << '
';
    return (0);
}

When we run this program with the data 3 7 3 0 2, the results are:

Threes 4 Sevens 1

(Your results may vary.)

switch Statement

The switch statement is similar to a chain of if-else statements. The general form of a switch statement is:

switch (expression) { 
    case constant1: 
        statement 
        . . . . 
        break; 

    case constant2: 
        statement 
        . . . . 
        // Fall through

    default: 
        statement 
        . . . . 
        break; 

    case constant3: 
        statement 
        . . . . 
        break; 
}

The switch statement evaluates the value of an expression and branches to one of the case statements. Duplicate statements are not allowed, so only one case will be selected. The expression must evaluate to an integer, character, or enumeration.

The case statements can be in any order and must be constants. The default statement can be put anywhere in the switch.

When C++ sees a switch statement, it evaluates the expression and then looks for a matching case statement. If none is found, the default statement is used. If no default is found, the statement does nothing.

A break statement inside a switch tells the computer to continue the execution after the switch. If the break is not there, execution continues with the next statement.

Tip

The switch statement is very similar to the PASCAL case statement. The main differences are that while PASCAL allows only one statement after the label, C++ allows many. C++ keeps executing until it hits a break statement or the end of the switch. In PASCAL you can’t “fall through” from one case to another. In C++ you can.

The calculator program in Chapter 7 contains a series of if-else statements.

        if (operator == '+') { 
            result += value; 
        } else if (operator == '-') { 
            result -= value; 
        } else if (operator == '*') { 
            result *= value; 
        } else if (operator == '/') { 
            if (value == 0) { 
                std::cout << "Error: Divide by zero
"; 
                std::cout << "   operation ignored
"; 
            } else 
                result /= value; 
        } else { 
            std::cout << "Unknown operator " << operator << '
';
        }

This section of code can easily be rewritten as a switch statement. In this switch, we use a different case for each operation. The default clause takes care of all the illegal operators.

Rewriting the program using a switch statement makes it not only simpler, but also easier to read, as seen in Example 8-5.

Example 8-5. calc-sw/calc3.cpp
#include <iostream>

int   result;      // the result of the calculations 
char  oper_char;   // operator the user specified 
int   value;       // value specified after the operator 

int main(  )
{
    result = 0;                 // initialize the result 

    // loop forever (or until break reached)
    while (true) {
        std::cout << "Result: " << result << '
';
        std::cout << "Enter operator and number: ";

        std::cin >> oper_char >> value;

        if ((oper_char == 'q') || (oper_char == 'Q'))
            break;

        switch (oper_char) {
            case '+':
                result += value;
                break;
            case '-':
                result -= value;
                break;
            case '*':
                result *= value;
                break;
            case '/':
                if (value == 0) {
                    std::cout << "Error:Divide by zero
";
                    std::cout << "   operation ignored
";
                } else
                    result /= value;
                break;
            default:
                std::cout << "Unknown operator " << oper_char << '
';
                break;
        }
    }
    return (0);
}

A break statement is not required at the end of a case. If the break is not there, execution will continue with the next statement.

For example:

control = 0; 

// A not so good example of programming 
switch (control) { 
        case 0: 
                std::cout << "Reset
"; 
        case 1: 
                std::cout << "Initializing
"; 
                break; 
        case 2: 
                std::cout "Working
"; 
}

In this case, when control == 0, the program prints:

Reset 
Initializing

Case 0 does not end with a break statement. After printing “Reset” the program falls through to the next statement (case 1) and prints “Initializing”.

But there is a problem with this syntax. You can’t be sure that the program is supposed to fall through from case 0 to case 1, or if the programmer forgot to put in a break statement. To clear up this confusion, a case section should always end with a break statement or the comment // fall through.

// A better example of programming
switch (control) { 
        case 0: 
                std::cout << "Reset
"; 
                // Fall through
        case 1: 
                std::cout << "Initializing
"; 
                break; 
        case 2: 
                std::cout << "Working
"; 
}

Because case 2 is last, it doesn’t absolutely need a break statement. A break would cause the program to skip to the end of the switch, but we’re already there.

But suppose we modify the program slightly and add another case to the switch:

// We have a little problem
switch (control) { 
        case 0: 
                std::cout << "Reset
"; 
                // Fall through 
        case 1: 
                std::cout << "Initializing
"; 
                break; 
        case 2: 
                std::cout << "Working
"; 
        case 3: 
                std::cout << "Closing down
"; 
}

Now when control == 2 the program prints:

Working 
Closing down

This is an unpleasant surprise. The problem is caused by the fact that case 2 is no longer the last case. We fall through. (Unintentionally, or otherwise we would have included a // Fall through comment.) A break is now necessary. If you always put in a break statement, you don’t have to worry about whether or not it is really needed.

// Almost there
switch (control) { 
        case 0: 
                std::cout << "Reset
"; 
                // Fall through
        case 1: 
                std::cout << "Initializing
"; 
                break; 
        case 2: 
                std::cout << "Working
"; 
                break; 
}

Finally, we ask the question: What happens when control == 5? In this case, since there is no matching case or a default clause, the entire switch statement is skipped.

In this example, the programmer did not include a default statement because control will never be anything but 0, 1, or 2. However, variables can get assigned strange values, so we need a little more defensive programming.

// The final version
switch (control) { 
    case 0: 
        std::cout << "Reset
"; 
        // Fall through
    case 1: 
        std::cout << "Initializing
"; 
        break; 
    case 2: 
        std::cout << "Working
"; 
        break; 
    default: 
        assert("Internal error: Impossible control value" != 0);
        break; 
}

In this case we’ve put in a default that triggers an assert failure. The test in this case is a C-style string (“Internal error....”) and a comparison (!= 0). Because of the way C++ stores strings, this test will always fail, which in turn will cause the assert to abort the program.

Although a default is not required, it should be put in every switch. Even though the default may be just:

default: 
        // Do nothing
        break;

it should be included. This indicates that you want to ignore out-of-range data.

switch, break, and continue

The break statement has two uses. Used inside a switch, it causes the program to exit the switch statement. Inside a for or while loop, it causes a loop exit. The continue statement is valid only inside a loop and causes the program to go to the top of the loop.

To illustrate how these statements work, we’ve produced a new version of the calculator program. The new program prints the result only after valid data is input, and it has a help command.

The help command is special. We don’t want to print the result after the help command, so instead of ending the help case with a break, we end it with a continue. The continue forces execution to go to the top of the loop.

When an unknown operator is entered, we print an error message. As with the help case, we use a continue statement to skip printing the result.

Finally, there is one special command: quit. This command is handled outside the switch. It is handled by the break at the top of the loop. Since the break is outside the switch, it belongs to the while loop and causes the program to exit the while.

The control flow for this program can be seen in Figure 8-2.

switch/continue
Figure 8-2. switch/continue

Programming Exercises

Exercise 8-1: Print a checkerboard (8-by-8 grid). Each square should be 5-by-3 characters wide. A 2-by-2 example follows:

+-----+-----+ 
|     |     | 
|     |     | 
|     |     | 
+-----+-----+ 
|     |     | 
|     |     | 
|     |     | 
+-----+-----+

Exercise 8-2: The total resistance of n resistors in parallel is:

1/R = 1/R1 + 1/R2 + 1/R3 + ... + 1/Rn

Suppose we have a network of two resistors with the values 400Ω and 200Ω. Our equation would be:

1/R = 1/R1 + 1/R2

Substituting in the value of the resistors we get:

1/R = 1/400 + 1/200

1/R = 3/400

R = 400/3

So the total resistance of our two-resistor network is 133.3Ω.

Write a program to compute the total resistance for any number of parallel resistors.

Exercise 8-3: Write a program to average n numbers.

Exercise 8-4: Write a program to print out the multiplication table.

Exercise 8-5: Write a program that reads a character and prints out whether it is a vowel or a consonant.

Exercise 8-6: Write a program that converts numbers to words. Example: 895 results in “eight nine five.”

Exercise 8-7: The number 85 is said “eighty-five,” not “eight five.” Modify the previous program to handle the numbers 0-100 so all numbers come out as we really say them. For example, 13 is “thirteen,” and 100 is “one hundred.”

Answers to Chapter Questions

Answer 8-1: The problem lies with the semicolon (;) at the end of the for statement. The body of the for statement is between the closing parentheses and the semicolon. In this case it is nothing. Even though the std::cout statement is indented, it is not part of the for statement. The indentation is misleading. The C++ compiler does not look at indentation. The program does nothing until the expression celsius <= 100 becomes false (celsius == 101). Then the std::cout is executed.

Answer 8-2: The problem is that we read the number into data[1] through data[5]. In C++ the range of legal array indices is 0 to <array size>-1, or in this case 0 to 4. data[5] is illegal. When we use it strange things happen; in this case the variable three_count is changed. The solution is to use only data[0] through data[4].

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.102.50