Em C, onde a origem parece ser, o bloco de código da instrução switch
não é uma construção especial. É um bloco de código normal, assim como um bloco sob uma instrução if
.
switch ()
{
}
if ()
{
}
case
e default
são marcadores de salto dentro desse bloco, especificamente relacionados a switch
. Eles são manipulados como rótulos de salto normais para goto
. Há uma regra específica que é importante aqui: os rótulos de salto podem estar em praticamente todos os lugares no código, sem interromper o fluxo de código.
Como um bloco de código normal, não precisa ser uma instrução composta. Os rótulos são opcionais também. Estas são instruções switch
válidas em C:
switch (a)
case 1: Foo();
switch (a)
Foo();
switch (a)
{
Foo();
}
O próprio padrão C dá isso como um exemplo (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.
Além disso, default
é um rótulo de salto também e, portanto, pode estar em qualquer lugar, sem a necessidade de ser o último caso.
Isso também explica o dispositivo de Duff:
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);
}
Por que o fall-through? Como no fluxo de código normal em um bloco de código normal, o fall-through para a próxima instrução é esperado , exatamente como você esperaria em um bloco de código if
.
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();
}
Meu palpite é que a razão para isso foi a facilidade de implementação. Você não precisa de código especial para analisar e compilar um bloco switch
, cuidando de regras especiais. Você apenas analisa como qualquer outro código e só precisa se preocupar com os rótulos e a seleção de pulos.
Uma interessante questão de acompanhamento disso tudo é se as seguintes instruções aninhadas imprimirem "Concluído". ou não.
int a = 10;
switch (a)
{
switch (a)
{
case 10: printf("Done.\n");
}
}
O padrão C cuida disso (6.8.4.2.4):
A case or default label is accessible only within the closest enclosing switch statement.