Menu

Comando switch em C: casos, break e fall-through

Como o switch do C escolhe um ramo a partir de uma lista de constantes - rótulos case, por que o break importa, o fall-through deliberado e o acidental, o default, switch sobre chars e enums, e quando o switch bate uma cadeia de else-if.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um valor, muitas constantes

Uma cadeia de else if que fica comparando a mesma variável contra constantes é um padrão que tem um comando dedicado em C:

if (choice == 1)      { /* ... */ }
else if (choice == 2) { /* ... */ }
else if (choice == 3) { /* ... */ }
else                  { /* ... */ }

O switch diz isso de forma mais direta. Ele avalia a expressão uma vez e depois salta direto para o rótulo correspondente.

Mude choice e rode de novo. A estrutura deixa visível de relance o conjunto de valores válidos, que é a verdadeira razão para preferi-la aqui.

Sintaxe e as regras dos rótulos case

switch (expressaoInteira) {
    case CONSTANTE_1:
        instrucoes
        break;
    case CONSTANTE_2:
        instrucoes
        break;
    default:
        instrucoes
        break;
}

Quatro restrições que o C impõe:

  • A expressão do switch precisa ter tipo inteiro - int, char, short, long ou um enum. Não float, não double e não string.
  • Todo rótulo case precisa ser uma expressão constante conhecida em tempo de compilação. case n: com n sendo uma variável é erro; case 3 + 4: e case MAX: (um #define ou constante de enum) são válidos.
  • Dois casos não podem ter o mesmo valor. Duplicatas são erro de compilação, o que é uma rede de proteção pequena mas genuína.
  • Um caso não consegue expressar uma faixa. case 1 ... 5: é uma extensão do GCC, não C padrão; use um if para faixas.

O default é opcional e pode aparecer em qualquer ponto do bloco, embora colocá-lo por último seja a convenção quase universal. Sem um default, um valor sem correspondência simplesmente pula o switch inteiro.

break: por que ele não é opcional

A coisa mais surpreendente do switch em C é que os rótulos case são alvos de salto, não caixas. Assim que a execução chega a um rótulo, ela continua por todas as instruções abaixo dele - atravessando outros rótulos case - até um break ou a chave de fechamento.

O primeiro switch imprime um, dois e tres - ele casou com case 1 e então executou tudo depois dele. O segundo imprime só um. Esse comportamento se chama fall-through, e esquecer um break é o clássico bug de switch em C: o programa fica silenciosamente errado em vez de falhar na compilação.

Compile com gcc -Wall -Wextra -Wimplicit-fallthrough e o compilador aponta todo caso que cai no seguinte.

O break no último caso é tecnicamente redundante - o bloco termina ali de qualquer forma. Escreva-o mesmo assim, para que acrescentar um caso novo abaixo dele depois não crie silenciosamente um fall-through.

Fall-through deliberado

O fall-through é um recurso quando é intencional. Empilhar rótulos sem instruções entre eles é a forma comum de dizer "estes valores fazem a mesma coisa":

Três rótulos compartilham um corpo, sem código duplicado e sem uma condição longa cheia de ||. Compiladores não avisam sobre essa forma, porque não há instruções por onde cair.

O fall-through com instruções no meio - em que o caso 1 executa e depois continua deliberadamente para o caso 2 - é mais raro e merece um comentário explícito, já que o leitor precisa ser informado de que o break ausente foi uma decisão:

switch (level) {
    case 3:
        printf("detalhado\n");
        /* fall through */
    case 2:
        printf("informativo\n");
        /* fall through */
    case 1:
        printf("erros\n");
        break;
}

/* fall through */ é o comentário que o aviso do GCC reconhece, então ele também silencia o diagnóstico.

Switch sobre caracteres

Como char é um tipo inteiro, a classificação de caracteres é um encaixe natural:

Repare que case 'a': usa aspas simples - uma constante de caractere, que é apenas o valor inteiro daquele caractere. case "a": seria uma string e não compila.

Switch sobre enums

A melhor combinação em C é switch com um enum. Os rótulos case viram nomes legíveis, e os compiladores conseguem avisar quando o switch não trata todos os enumeradores:

Duas coisas para copiar daqui. Cada caso faz return, então nenhum break é necessário - um return sai da função inteira, o que também encerra o switch. E não há default: com -Wswitch (incluído em -Wall), deixar o default de fora faz o compilador avisar no dia em que alguém acrescentar um quinto estado e esquecer desta função. Um default teria escondido isso.

Declarando variáveis dentro de um switch

O corpo de um switch é um bloco só, então uma declaração num caso fica visível - embora não necessariamente inicializada - nos que vêm depois. Saltar por cima de uma inicialização é erro em C:

switch (n) {
    case 1:
        int x = 10;        // erro em C: um rotulo nao pode preceder uma declaracao assim
        printf("%d\n", x);
        break;
    case 2:
        printf("%d\n", x); // x existe aqui, mas nunca foi inicializada
        break;
}

Envolva o caso nas próprias chaves quando ele precisar de variáveis locais:

switch (n) {
    case 1: {
        int x = 10;
        printf("%d\n", x);
        break;
    }
    case 2:
        printf("dois\n");
        break;
}

switch ou if-else?

Recorra ao switch quando tudo isto for verdade: você está testando uma expressão, ela tem tipo inteiro e você a compara contra constantes fixas. Tratamento de menus, máquinas de estado com enum, classificação de caracteres e despacho de comandos são os casos canônicos. O ganho é legibilidade, checagem de valores duplicados e um compilador capaz de avisar sobre enumeradores não tratados.

Recorra ao if-else quando os testes envolverem faixas (score >= 90), valores de ponto flutuante, strings (você precisa do strcmp), várias variáveis diferentes, ou condições combinadas com && e ||. Nenhuma dessas pode ser expressa como um rótulo case.

O desempenho raramente é o fator decisivo. Compiladores transformam um switch denso numa tabela de saltos e um esparso em comparações, então com um punhado de casos as duas formas são indistinguíveis. Escolha a que declara a intenção.

Perguntas frequentes

Como funciona o comando switch em C?

switch (expr) avalia uma expressão inteira uma vez, salta para o rótulo case cuja constante corresponde, e executa dali em diante até encontrar um break ou o fim do switch. Se nada corresponder, ele salta para o default quando existe um, e caso contrário pula o bloco inteiro.

Por que preciso do break num switch em C?

Porque rótulos case são alvos de salto, não blocos separados. Sem um break, a execução continua direto para as instruções do próximo caso - isso se chama fall-through. Um break esquecido é o bug de switch mais comum em C, e o gcc -Wimplicit-fallthrough o sinaliza.

Dá para usar switch com strings em C?

Não. Um switch em C exige uma expressão inteira, então valores int, char e enum funcionam, mas strings, floats e faixas não. Para ramificar sobre uma string, use strcmp numa cadeia de if-else, ou mapeie a string para um enum antes e faça o switch sobre ele.

Quando devo usar switch em vez de if-else em C?

Use switch quando estiver comparando um inteiro ou caractere contra uma lista de constantes fixas - comandos de menu, estados de enum, classificação de caracteres. Use uma cadeia de if-else quando os testes envolverem faixas, várias variáveis, ou qualquer coisa além da igualdade com uma constante de tempo de compilação.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR