Section 6.6. The switch Statement


6.6. The switch Statement

Deeply nested if else statements can often be correct syntactically and yet not correctly reflect the programmer's logic. For example, mistaken else if matchings are more likely to pass unnoticed. Adding a new condition and associated logic or making other changes to the statements is also hard to get right. A switch statement provides a more convenient way to write deeply nested if/else logic.

Suppose that we have been asked to count how often each of the five vowels appears in some segment of text. Our program logic is as follows:

  • Read each character until there are no more characters to read

  • Compare each character to the set of vowels

  • If the character matches one of the vowels, add 1 to that vowel's count

  • Display the results

The program was used to analyze this chapter. Here is the output:

      Number of vowel a: 3499      Number of vowel e: 7132      Number of vowel i: 3577      Number of vowel o: 3530      Number of vowel u: 1185 

6.6.1. Using a switch

We can solve our problem most directly using a switch statement:

      char ch;      // initialize counters for each vowel      int aCnt = 0, eCnt = 0, iCnt = 0,          oCnt = 0, uCnt = 0;      while (cin >> ch) {          // if ch is a vowel, increment the appropriate counter          switch (ch) {              case 'a':                  ++aCnt;                  break;              case 'e':                  ++eCnt;                  break;              case 'i':                  ++iCnt;                  break;              case 'o':                  ++oCnt;                  break;              case 'u':                  ++uCnt;                  break;          }      }      // print results      cout  << "Number of vowel a: \t" << aCnt << '\n'            << "Number of vowel e: \t" << eCnt << '\n'            << "Number of vowel i: \t" << iCnt << '\n'            << "Number of vowel o: \t" << oCnt << '\n'            << "Number of vowel u: \t" << uCnt << endl; 

A switch statement executes by evaluating the parenthesized expression that follows the keyword switch. That expression must yield an integral result. The result of the expression is compared with the value associated with each case. The case keyword and its associated value together are known as the case label. Each case label's value must be a constant expression (Section 2.7, p. 62). There is also a special case label, the default label, which we cover on page 203.

If the expression matches the value of a case label, execution begins with the first statement following that label. Execution continues normally from that statement through the end of the switch or until a break statement. If no match is found, (and if there is no default label), execution falls through to the first statement following the switch. In this program, the switch is the only statement in the body of a while. Here, falling through the switch returns control to the while condition.

We'll look at break statements in Section 6.10 (p. 212). Briefly, a break statement interrupts the current control flow. In this case, the break TRansfers control out of the switch. Execution continues at the first statement following the switch. In this example, as we already know, transferring control to the statement following the switch returns control to the while.

6.6.2. Control Flow within a switch

It is essential to understand that execution flows across case labels.

It is a common misunderstanding to expect that only the statements associated with the matched case label are executed. However, execution continues across case boundaries until the end of the switch statement or a break is encountered.



Sometimes this behavior is indeed correct. We want to execute the code for a particular label as well as the code for following labels. More often, we want to execute only the code particular to a given label. To avoid executing code for subsequent cases, the programmer must explicitly tell the compiler to stop execution by specifying a break statement. Under most conditions, the last statement before the next case label is break. For example, here is an incorrect implementation of our vowel-counting switch statement:

      // warning: deliberately incorrect!      switch (ch) {           case 'a':                ++aCnt;   // oops: should have a break statement           case 'e':                ++eCnt;   // oops: should have a break statement           case 'i':                ++iCnt;   // oops: should have a break statement           case 'o':                ++oCnt;   // oops: should have a break statement           case 'u':                ++uCnt;   // oops: should have a break statement      } 

To understand what happens, we'll trace through this version assuming that value of ch is 'i'. Execution begins following case 'i'thus incrementing iCnt. Execution does not stop there but continues across the case labels incrementing oCnt and uCnt as well. If ch had been 'e', then eCnt, iCnt, oCnt, and uCnt would all be incremented.

Forgetting to provide a break is a common source of bugs in switch statements.



Although it is not strictly necessary to specify a break statement after the last label of a switch, the safest course is to provide a break after every label, even the last. If an additional case label is added later, then the break is already in place.



break Statements Aren't Always Appropriate

There is one common situation where the programmer might wish to omit a break statement from a case label, allowing the program to fall through multiple case labels. That happens when two or more values are to be handled by the same sequence of actions. Only a single value can be associated with a case label. To indicate a range, therefore, we typically stack case labels following one another. For example, if we wished only to count vowels seen rather than count the individual vowels, we might write the following:

      int vowelCnt = 0;      // ...      switch (ch)      {          // any occurrence of a,e,i,o,u increments vowelCnt          case 'a':          case 'e':          case 'i':          case 'o':          case 'u':              ++vowelCnt;              break;      } 

Case labels need not appear on a new line. We could emphasize that the cases represent a range of values by listing them all on a single line:

      switch (ch)      {          // alternative legal syntax          case 'a': case 'e': case 'i': case 'o': case 'u':              ++vowelCnt;              break;      } 

Less frequently, we deliberately omit a break because we want to execute code for one case and then continue into the next case, executing that code as well.

Deliberately omitting a break at the end of a case happens rarely enough that a comment explaining the logic should be provided.



6.6.3. The default Label

The default label provides the equivalent of an else clause. If no case label matches the value of the switch expression and there is a default label, then the statements following the default are executed. For example, we might add a counter to track how many nonvowels we read. We'll increment this counter, which we'll name otherCnt, in the default case:

          // if ch is a vowel, increment the appropriate counter          switch (ch) {              case 'a':                  ++aCnt;                  break;              // remaining vowel cases as before              default:                  ++otherCnt;                  break;          }      } 

In this version, if ch is not a vowel, execution will fall through to the default label, and we'll increment otherCnt.

It can be useful always to define a default label even if there is no processing to be done in the default case. Defining the label indicates to subsequent readers that the case was considered but that there is no work to be done.



A label may not stand alone; it must precede a statement. If a switch ends with the default case in which there is no work to be done, then the default label must be followed by a null statement.

6.6.4. switch Expression and Case Labels

The expression evaluated by a switch can be arbitrarily complex. In particular, the expression can define and intialize a variable:

      switch(int ival = get_response()) 

In this case, ival is initialized, and the value of ival is compared with each case label. The variable ival exists throughout the entire switch statement but not outside it.

Case labels must be constant integral expressions (Section 2.7, p. 62). For example, the following labels result in compile-time errors:

      // illegal case label values      case 3.14:  // noninteger      case ival:  // nonconstant 

It is also an error for any two case labels to have the same value.

6.6.5. Variable Definitions inside a switch

Variables can be defined following only the last case or default label:

      case true:           // error: declaration precedes a case label           string file_name = get_file_name();           break;      case false:           // ... 

The reason for this rule is to prevent code that might jump over the definition and initialization of a variable.

Recall that a variable can be used from its point of definition until the end of the block in which it is defined. Now, consider what would happen if we could define a variable between two case labels. That variable would continue to exist until the end of the enclosing block. It could be used by code in case labels following the one in which it was defined. If the switch begins executing in one of these subsequent case labels, then the variable might be used even though it had not been defined.

If we need to define a variable for a particular case, we can do so by defining the variable inside a block, thereby ensuring that the variable can be used only where it is guaranteed to have been defined and initialized:

      case true:          {              // ok: declaration statement within a statement block              string file_name = get_file_name();              // ...          }        break;          case false:              // ... 



C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net