I l@ve RuBoard Previous Section Next Section

8.2 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.

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\n"; 
                std::cout << "   operation ignored\n"; 
            } else 
                result /= value; 
        } else { 
            std::cout << "Unknown operator " << operator << '\n';
        } 

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 << '\n';
        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\n";
                    std::cout << "   operation ignored\n";
                } else
                    result /= value;
                break;
            default:
                std::cout << "Unknown operator " << oper_char << '\n';
                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\n"; 
        case 1: 
                std::cout << "Initializing\n"; 
                break; 
        case 2: 
                std::cout "Working\n"; 
} 

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\n"; 
                // Fall through
        case 1: 
                std::cout << "Initializing\n"; 
                break; 
        case 2: 
                std::cout << "Working\n"; 
} 

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\n"; 
                // Fall through 
        case 1: 
                std::cout << "Initializing\n"; 
                break; 
        case 2: 
                std::cout << "Working\n"; 
        case 3: 
                std::cout << "Closing down\n"; 
} 

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\n"; 
                // Fall through
        case 1: 
                std::cout << "Initializing\n"; 
                break; 
        case 2: 
                std::cout << "Working\n"; 
                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\n"; 
        // Fall through
    case 1: 
        std::cout << "Initializing\n"; 
        break; 
    case 2: 
        std::cout << "Working\n"; 
        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.

    I l@ve RuBoard Previous Section Next Section