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,longou umenum. Nãofloat, nãodoublee não string. - Todo rótulo
caseprecisa ser uma expressão constante conhecida em tempo de compilação.case n:comnsendo uma variável é erro;case 3 + 4:ecase MAX:(um#defineou 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 umifpara 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.