In C, where the origin seems to be, the code block of the switch
statement is not a special construct. It is a normal block of code, just as a block under an if
statement.
switch ()
{
}
if ()
{
}
case
and default
are jump labels inside this block, specifically related to switch
. They are handled just as normal jump labels for goto
. There is one specific rule that is important here: Jump labels can be nearly everywhere in the code, without interrupting the code flow.
As a normal code block, it doesn't need to be a compound statement. The labels are optional, too. These are valid switch
statements in C:
switch (a)
case 1: Foo();
switch (a)
Foo();
switch (a)
{
Foo();
}
The C standard itself gives this as an example (6.8.4.2):
switch (expr)
{
int i = 4;
f(i);
case 0:
i=17;
/*falls through into default code */
default:
printf("%d\n", i);
}
In the artificial program fragment, the object whose identifier is i exists
with automatic storage duration (within the block) but is never initialized,
and thus if the controlling expression has a nonzero value, the call to the
printf function will access an indeterminate value. Similarly, the call to the
function f cannot be reached.
Furthermore, default
is a jump label, too, and thus can be anywhere, without the need to be the last case.
This also explains Duff's Device:
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);
}
Why the fall-through? Because in the normal code flow in a normal block of code, fall-through to the next statement is expected, just as you would expect it in an if
code block.
if (a == b)
{
Foo();
/* "Fall-through" to Bar expected here. */
Bar();
}
switch (a)
{
case 1:
Foo();
/* Automatic break would violate expected code execution semantics. */
case 2:
Bar();
}
My guess is that the reason for this was ease of implementation. You don't need special code for parsing and compiling a switch
block, caring for special rules. You just parse it like any other code and only have to care for the labels and the jump selection.
An interesting follow-up question from all this is if the following nested statements print "Done." or not.
int a = 10;
switch (a)
{
switch (a)
{
case 10: printf("Done.\n");
}
}
The C standard cares for this (6.8.4.2.4):
A case or default label is accessible only within the closest enclosing switch statement.
CASE
statement equivalently to a giant if/elseif block.case 'a': case 'A': case 'b': case 'B'
but mostly because I cannot docase in [ 'a', 'A', 'b', 'B' ]
. A slightly better question is, in my current preferred language (C#), the break is mandatory, and there is no implicit fall-though; forgettingbreak
is a syntax error... :\case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/
break
is present anywhere” is a significantly simpler rule to implement than “Don’t emit a jump iffallthrough
is present in aswitch
”.