Previous Table of Contents Next


7.2.9.6. Jumps

The break, continue, and goto statements provide for explicit transfer of control. Executing

   break;

jumps to the point immediately after the end of the nearest enclosing while, for, do, or switch statement; executing

   continue;

jumps back to the beginning of the next iteration (including the test) in the nearest enclosing for, while, or do statement. So, for example, if we want to do something to each of the nonzero elements of an n-element array x, we might write

   for (int i = 0; i < n; ++i) {
         if (x[i] != 0) {
              // do something to x[i]
         }
}

Equivalently, we might write

   for (int i = 0; i < n; ++i) {
         if (x[i])
              continue;
         // do something to x[i]
   }

Here, the continue statement ends the current iteration and starts the next one. Similarly, if we wanted to find the index of the first nonzero element, if any, we might write

   int i = 0;
   while (i < n && x[i] == 0)
         ++i;

or we might write

   int i;
   for (i = 0; i < n; ++i)
         if (x[i])
               break;

In the latter example, i is defined outside the for statement to force its scope to include whatever might follow the for statement.

The goto statement behaves similarly to such statements in other languages. The target of a goto is a label, which is an identifier followed by a colon. Labels can have the same names as other entities without ambiguity, because the only place they can ever appear is before a colon or after a goto keyword. The scope of a label is the entire function in which it appears, which implies that it is possible to jump from outside a block to inside it. However, such a jump cannot bypass the initialization of a variable.

The last goto I wrote in a real program was in 1980 or so, where it was used to restart an iteration from inside a switch statement. To understand it, we must first understand the switch statement.

7.2.9.7. Switches

A switch is a way of doing a multiway jump based on the value of an integral expression (which, in practice, often yields a character or a value of enumerated type). Strictly speaking, the form is

   switch (expression) statement

but in practice, the statement is almost always a block.

Within that block may appear labels of the form

   case expression:

with the requirement that each of the expressions must be an integral expression whose value can be determined during compilation, and all the expressions must have distinct values.

In addition, the label

   default:

may appear, but no more than once.

Executing a switch statement evaluates the parenthesized expression and jumps to the case label whose value matches it. If there is no match, control passes to the default: label, if any, or to the point immediately after the entire switch statement.

Because case labels are just labels, control will flow from one to the next unless the programmer takes explicit action to prevent it from doing so. The usual such action is to use a break statement before each case label after the first. For example:

   int day;
   char* day_name;

   // ...

   switch (day) {

   case 0:
         day_name = “Sunday”;
         break;

   case 1:
         day_name = “Monday”;
         break;

   case 2:
         day_name = “Tuesday”;
         break;

   case 3:
         day_name = “Wednesday”;
         break;

   case 4:
         day_name = “Thursday”;
         break;

   case 5:
         day_name = “Friday”;
         break;

   case 6:
         day_name = “Saturday”;
         break;

   default:
         day_name = “???”;
   }

If day is negative, or greater than 6, day_name will be set to (the address of the initial character of a null-terminated array initialized with) ??? by virtue of the default label. If that label, and its corresponding assignment, did not appear, day_name would have retained its previous value.

As another example, suppose that p points to an element of a character array, and that immediately following this element is the first of a (possibly empty) sequence of +, -, #, or space characters. Suppose further that you want to scan this sequence of characters and set one or more of the variables plus, minus, sharp, or space to true, depending on which of the characters appears in the sequence. You want to stop scanning as soon as you encounter a character that is not one of these, but not before—even if you encounter all four characters and therefore know that all four variables will be true—because the value of p after you’re finished is important.

Here is one way to solve this problem:

   while (true) {
         if (*++p == ‘+’)
              plus = true;
         else if (*p = ‘-’)
              minus = true;
         else if (*p = ‘ ‘)
              space = true;
         else if (*p = ‘#’)
              sharp = true;
         else break;
   }

However, most compilers implement this sequence of tests as written, where a switch might well generate faster code. We could write an appropriate switch statement this way:

   while (true) {
         switch (*++p) {

         case ‘+’: plus = true; continue;
         case ‘-’: minus = true; continue;
         case ‘ ‘: space = true; continue;
         case ‘#’: sharp = true; continue;
         default:
              break;      // Wrong!!
         }
   }

but this strategy does not work because the break statement exits from the switch but not from the while. I believe that the least convoluted way to write this particular example efficiently is to use a goto:

top:
      switch(*++p) {

      case ‘+’: plus = true; goto top;
      case ‘-’: minus = true; goto top;
      case ‘ ‘: space = true; goto top;
      case ‘#’: sharp = true; goto top;

      }

If the character is none of the four we care about, control passes to the statement after the switch.


Previous Table of Contents Next