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 The block can also be pictured as the block spanning more than one case statement.
C99, Section 6.8.6.1 "The goto statement", says1 of the C Standard [ISO/IEC 9899:2011] 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 such intermixing creates code that is, at best, confusing and unclear in what it does, which can cause undesirable behavior.
The examples here fall under the exception MSC17-EX2 in recommendation MSC17-C. Finish every set of statements associated with a case label with a break statement.
...
This example shows the use of the switch
statement to jump into a for
loop.
Code Block | ||||
---|---|---|---|---|
| ||||
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 is executed. 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:
...
The compliant example separates the switch
and for
blocks.
Code Block | ||||
---|---|---|---|---|
| ||||
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 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:
Code Block |
---|
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 device unrolls this loop to minimize the number of comparisons performed:
Code Block | ||||
---|---|---|---|---|
| ||||
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 is supported by all compliant compilers. When describing Duff's Devicedevice, 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.
...
This is an alternate implemention of Duff's Device device, which separates the switch
statement and loop.
Code Block | ||||
---|---|---|---|---|
| ||||
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++;
}
|
...
CERT C++ Secure Coding Standard: MSC20-CPP. Do not use a switch statement to transfer control into a complex block
ISO/IEC 9899:1999 Section 2011 Section 6.8.6.1 "The goto
statement"
MISRA Rule 15.1
...