You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

A switch statement can be mixed with a block of code by starting the block in one case label, then having another case label within the block. This can also be pictured as the block spanning more than one case statement.

C99, Section 6.8.6.1 "The goto statement", says:

A goto statement shall not jump from outside the scope of an identifier having a variably modified type to inside the scope of that identifier.

However, the standard does not disallow jumping via goto or switch into loops that do not involve variably modified type identifiers. Consequently, loops and other blocks can be freely intermixed with switch statements. Unfortunately, this creates code that is at best confusing and unclear in what it does, which may cause undesirable behavior.

The examples here fall under the exception MSC17:EX2 in MSC17-C. Finish every set of statements associated with a case label with a break statement.

Noncompliant Code Example

This example shows the use of the switch statment to jump into a for loop.

int f(int i) {
  int j=0;
  switch (i) {
    case 1:
      for(j=0;j<10;j++) {
      // no break, process case 2 as well
    case 2: // switch jumps inside the for block
        j++;
      // no break, process case 3 as well
    case 3:
        j++;
      }
      break;
  default:
    // default action
    break;
  }
  return j;
}

Implementation Details

When i=1, this executes the entire for loop. When i=2, two increments to j are made before the loop starts. When i=3, one increment to j is made before the loop starts. The default case is no loop. Consequently, the function has the following behavior:

i

f(i)

1

12

2

12

3

11

other values

0

Compliant Code Example

The compliant example separates the switch and for blocks.

int f(int i) {
  int j=0;
  switch (i) {
    case 1:
      // no break, process case 2 as well
    case 2:
      j++;
      // no break, process case 3 as well
    case 3:
      j++;
      break;
    default:
      // default action
      return j;
  }
  for(j++;j<10;j++) {
    j+=2;
  }
  return j;
}

Noncompliant Code Example (Duff's Device)

Duff's Device is a curious optimization applied to code intended to perform a serial copy. That is, it copies a a series of bytes into one memory output in turn. A simple code to do this would be:

size_t count; /* must be nonzero */
char *to;     /* output destination */
char *from;   /* Points to count bytes to copy */

do {
  *to = *from++;        /* Note that the ''to'' pointer is NOT incremented */
} while (--count > 0);

However, this code might be unacceptably slow because the while condition is performed count times. The classic code for Duff's Device unrolls this loop to minimize the number of comparisons performed:

int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7:      *to = *from++;
case 6:      *to = *from++;
case 5:      *to = *from++;
case 4:      *to = *from++;
case 3:      *to = *from++;
case 2:      *to = *from++;
case 1:      *to = *from++;
	   } while (--n > 0);
}

In this code, the first iteration of the loop is subject to the switch statement, so it performs count % 8 assignments. Each subsequent iteration of the loop performs 8 assignments (being outside the loop the switch statement is ignored). Consequently, this code performs count assignments, but only n comparisons, so it is usually faster.

The code is widely considered to be legal C and C++ and supported by all compliant compilers. When describing Duff's Device, the creator noted:

Many people ... have said that the worst feature of C is that switches don't break automatically before each case label. This code forms some sort of argument in that debate, but I'm not sure whether it's for or against.

Compliant Code Example (Duff's Device)

This is an alternate implemention of Duff's Device which separates the switch statement and loop.

int n = (count + 7) / 8;
switch (count % 8) {
case 0: *to = *from++; /* fall through */
case 7: *to = *from++; /* fall through */
case 6: *to = *from++; /* fall through */
case 5: *to = *from++; /* fall through */
case 4: *to = *from++; /* fall through */
case 3: *to = *from++; /* fall through */
case 2: *to = *from++; /* fall through */
case 1: *to = *from++; /* fall through */
}
while (--n > 0) {
  *to = *from++;
  *to = *from++;
  *to = *from++;
  *to = *from++;
  *to = *from++;
  *to = *from++;
  *to = *from++;
  *to = *from++;
}

Risk Assessment

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC20-C

medium

probable

medium

P8

L2

Other Languages

This rule appears in the C++ Secure Coding Standard as MSC20-CPP. Do not use a switch statement to transfer control into a complex block.

References

[[ISO/IEC 9899:1999]] Section 6.8.6.1 "The goto statement"
[MISRA 04] Rule 15.1
Tom Duff on Duff's Device


      49. Miscellaneous (MSC)      MSC21-C. Use inequality to terminate a for loop

  • No labels