C++ switch
Um caixa eletrônico mostra: "1 - Saldo, 2 - Depósito, 3 - Sair". Dá para tratar cada opção com else if (if (opcao == 1) … else if (opcao == 2) …), mas, quando você compara uma variável com uma lista de valores exatos, o C++ tem uma estrutura feita sob medida: o switch ("chave", como a chave de trilhos que desvia o trem). Nesta aula você vai montar o menu do caixa, entender a famosa "queda" de um case para o outro e conhecer os erros que o switch provoca quando usado fora do que ele aceita.
- As peças do
switch:case,breakedefault. - O que acontece quando falta o
break(o fall-through) e o aviso-Wimplicit-fallthrough. - Como juntar vários valores na mesma resposta e como cair de propósito com
[[fallthrough]]. - Por que o
switchsó aceita inteiros,chareenum(e o erro comstd::string). - O erro
jump to case labelao criar uma variável dentro de umcase, e o conserto com{ }.
Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), com g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.
A ideia: o elevador
No painel de um elevador, você aperta um número exato: 1, 2, 3. O elevador não pergunta "o andar é maior que 2?"; ele vai direto ao andar pedido. Se você apertar um botão que não existe, nada acontece (ou toca um alarme).
O switch é assim: ele olha o valor de uma variável e pula direto para o case ("caso") com aquele valor. Se nenhum case combinar, vai para o default ("padrão"), o "alarme" para valores inesperados.
1. O menu do caixa eletrônico
#include <iostream>
int main() {
int opcao = 0;
std::cout << "1-Saldo 2-Depósito 3-Sair: ";
std::cin >> opcao;
switch (opcao) {
case 1:
std::cout << "Seu saldo é R$ 150\n";
break;
case 2:
std::cout << "Quanto quer depositar?\n";
break;
case 3:
std::cout << "Até logo!\n";
break;
default:
std::cout << "Opção inválida\n";
}
}
- switch (opcao)
- A variável a ser examinada, entre parênteses. O
switchcalcula o valor dela uma vez. - { … }
- O corpo do
switch, com todos os casos dentro de um único par de chaves. - case 1:
- Um rótulo: "se o valor for 1, comece aqui". Termina com dois-pontos, não com ponto e vírgula. O valor precisa ser uma constante (um número escrito no código, um
charcomo'S', umaconst). - break;
- "Quebre": sai do
switche continua depois da}. Sem ele, o programa continua executando ocasede baixo (seção 2). - default:
- Para qualquer valor que não tenha
case. É opcional, mas quase sempre útil. Como é o último, não precisa debreak(muita gente coloca assim mesmo, por hábito).
$ g++ -std=c++23 -Wall -Wextra menu.cpp -o menu $ ./menu 1-Saldo 2-Depósito 3-Sair: 1 « a Ana digitou 1 e Enter Seu saldo é R$ 150 $ ./menu 1-Saldo 2-Depósito 3-Sair: 3 Até logo! $ ./menu 1-Saldo 2-Depósito 3-Sair: 9 Opção inválida
O mesmo menu com else if funcionaria igual. A vantagem do switch é a leitura: fica claro que tudo depende de uma variável só, e cada valor aparece alinhado, como numa tabela. Para muitas opções, o compilador também consegue gerar um código que pula direto ao caso certo, sem testar um por um.
2. Sem break: o programa "cai" no próximo caso
Este é o comportamento mais estranho do switch, herdado da linguagem C. O case marca só onde começar; não marca onde parar. Quem para é o break. Veja o menu sem nenhum break, com a opção 1:
#include <iostream>
int main() {
int opcao = 1;
switch (opcao) {
case 1:
std::cout << "Seu saldo é R$ 150\n";
case 2:
std::cout << "Quanto quer depositar?\n";
case 3:
std::cout << "Até logo!\n";
default:
std::cout << "Opção inválida\n";
}
}
$ g++ -std=c++23 -Wall -Wextra cai.cpp -o cai
cai.cpp: In function ‘int main()’:
cai.cpp:8:26: warning: this statement may fall through [-Wimplicit-fallthrough=]
8 | std::cout << "Seu saldo é R$ 150\n";
| ^~~~~~~~~~~~~~~~~~~~~~
cai.cpp:9:9: note: here
9 | case 2:
| ^~~~
cai.cpp:10:26: warning: this statement may fall through [-Wimplicit-fallthrough=]
10 | std::cout << "Quanto quer depositar?\n";
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
cai.cpp:11:9: note: here
11 | case 3:
| ^~~~
cai.cpp:12:26: warning: this statement may fall through [-Wimplicit-fallthrough=]
12 | std::cout << "Até logo!\n";
| ^~~~~~~~~~~~~
cai.cpp:13:9: note: here
13 | default:
| ^~~~~~~
$ ./cai
Seu saldo é R$ 150
Quanto quer depositar? « caiu no case 2…
Até logo! « …no case 3…
Opção inválida « …e no default
O switch pulou para o case 1 e foi executando tudo para baixo, passando por cima dos outros rótulos como se eles não existissem. Isso se chama fall-through ("cair através").
- warning: this statement may fall through
- "Esta instrução pode cair para o próximo caso".
- note: here
- "Aqui": mostra o
caseem que ela vai cair. - [-Wimplicit-fallthrough=]
- O nome do aviso. No g++, ele vem com o
-Wextra: mais um motivo para nunca compilar sem ele.
No editor do guia (Clang), esse aviso não vem com -Wall -Wextra: é preciso pedir -Wimplicit-fallthrough. A caixa abaixo já pede. A mensagem do Clang é outra: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] ("queda não anotada entre rótulos do switch").
Missão: com a opção 2, este menu mostra três mensagens e três avisos. Coloque os break que faltam para que apareça só Quanto quer depositar? e os avisos sumam.
#include <iostream>
int main() {
int opcao = 2;
switch (opcao) {
case 1:
std::cout << "Seu saldo é R$ 150\n";
case 2:
std::cout << "Quanto quer depositar?\n";
case 3:
std::cout << "Até logo!\n";
default:
std::cout << "Opção inválida\n";
}
}
#include <iostream>
int main() {
int opcao = 2;
switch (opcao) {
case 1:
std::cout << "Seu saldo é R$ 150\n";
break;
case 2:
std::cout << "Quanto quer depositar?\n";
break;
case 3:
std::cout << "Até logo!\n";
break;
default:
std::cout << "Opção inválida\n";
}
}
3. Quando a queda é útil
Vários valores, a mesma resposta
Rótulos colados, sem nada entre eles, são o jeito de dizer "para este ou este valor":
#include <iostream>
int main() {
char resposta = 's';
switch (resposta) {
case 'S':
case 's':
std::cout << "Confirmado\n";
break;
case 'N':
case 'n':
std::cout << "Cancelado\n";
break;
default:
std::cout << "Responda S ou N\n";
}
}
$ g++ -std=c++23 -Wall -Wextra grupos.cpp -o grupos $ ./grupos Confirmado « 's' minúsculo caiu no mesmo bloco do 'S'
Aqui não há aviso: um case vazio caindo no próximo é obviamente proposital. Repare nas aspas simples: 'S' é um caractere (char, aula Caracteres), que por dentro é um número, e por isso o switch o aceita.
Cair de propósito: [[fallthrough]] C++17
Às vezes você quer mesmo que um caso com código continue no de baixo. Por exemplo, nos planos de uma empresa de nuvem, cada plano inclui tudo o que os planos menores têm:
#include <iostream>
int main() {
int plano = 2;
std::cout << "Seu plano inclui:\n";
switch (plano) {
case 3:
std::cout << "- suporte 24h\n";
[[fallthrough]];
case 2:
std::cout << "- backup diário\n";
[[fallthrough]];
case 1:
std::cout << "- 10 GB de espaço\n";
break;
default:
std::cout << "- nada (plano inválido)\n";
}
}
$ g++ -std=c++23 -Wall -Wextra proposital.cpp -o proposital $ ./proposital Seu plano inclui: - backup diário « começou no case 2… - 10 GB de espaço « …caiu no case 1 e parou no break
- [[fallthrough]];
- Um atributo (as instruções entre colchetes duplos são recados para o compilador). Ele diz: "a queda aqui é proposital". O aviso some, e quem lê o código entende que não é esquecimento. Vai sempre logo antes do próximo
case, com;.
Antes do C++17, os programadores escreviam um comentário // fall through no mesmo lugar; o g++ ainda reconhece esse comentário e não avisa. Prefira o atributo, que é padrão e funciona em todo compilador.
4. O que o switch aceita (e o que não aceita)
A variável do switch precisa ser de um tipo inteiro: int, long, char, bool, ou um enum. E os valores dos case precisam ser constantes conhecidas na compilação.
Texto não: switch com std::string
#include <iostream>
#include <string>
int main() {
std::string fruta = "maçã";
switch (fruta) {
case "maçã":
std::cout << "R$ 4,50 o kg\n";
break;
}
}
$ g++ -std=c++23 -Wall -Wextra texto.cpp -o texto
texto.cpp: In function ‘int main()’:
texto.cpp:7:13: error: switch quantity not an integer
7 | switch (fruta) {
| ^~~~~
"A quantidade do switch não é um inteiro". Diferente de Java, C# ou JavaScript, o switch do C++ não compara textos. Para escolher por texto, use if (fruta == "maçã") … else if …. (Quando você conhecer o std::map, no Nível 2, vai ver um jeito ainda melhor para tabelas de preços.)
Número com vírgula também não
$ g++ -std=c++23 -Wall -Wextra dbl.cpp -o dbl « double nota = 7.5; switch (nota) { case 7.5: … }
dbl.cpp: In function ‘int main()’:
dbl.cpp:5:13: error: switch quantity not an integer
5 | switch (nota) {
| ^~~~
Notas, preços e outras faixas são trabalho do else if, com >= e <.
O valor do case precisa ser constante
#include <iostream>
int main() {
int senha = 1234;
int digitado = 1234;
switch (digitado) {
case senha:
std::cout << "Entrou\n";
break;
}
}
$ g++ -std=c++23 -Wall -Wextra variavelcase.cpp -o variavelcase
variavelcase.cpp: In function ‘int main()’:
variavelcase.cpp:7:14: error: the value of ‘senha’ is not usable in a constant expression
7 | case senha:
| ^~~~~
variavelcase.cpp:4:9: note: ‘int senha’ is not const
4 | int senha = 1234;
| ^~~~~
…(a mesma mensagem aparece duas vezes)…
"O valor de senha não pode ser usado numa expressão constante", e a nota explica: "senha não é const". Uma variável comum pode mudar durante o programa, e os rótulos do switch são fixados na compilação. Com const int senha = 1234; (aula Constantes), compila e mostra Entrou. Se o valor só é conhecido ao rodar (digitado pelo usuário, lido de um arquivo), use if.
O switch com enum confere se você esqueceu algum caso
Um enum class (aula Enums) é um tipo com uma lista fechada de valores, como as cores do semáforo. Com ele, o -Wall avisa quando um valor ficou sem case:
#include <iostream>
enum class Cor { Verde, Amarelo, Vermelho };
int main() {
Cor semaforo = Cor::Amarelo;
switch (semaforo) {
case Cor::Verde:
std::cout << "Siga\n";
break;
case Cor::Vermelho:
std::cout << "Pare\n";
break;
}
}
$ g++ -std=c++23 -Wall -Wextra enum.cpp -o enum
enum.cpp: In function ‘int main()’:
enum.cpp:7:12: warning: enumeration value ‘Amarelo’ not handled in switch [-Wswitch]
7 | switch (semaforo) {
| ^
$ ./enum
« Amarelo não tem case: nada aparece
"O valor Amarelo da enumeração não é tratado no switch". Essa conferência é um dos melhores motivos para usar switch com enum: se alguém acrescentar uma cor nova ao enum, o compilador aponta cada switch que precisa ser atualizado.
5. Criar uma variável dentro de um case
Parece natural criar uma variável dentro de um caso. Mas:
#include <iostream>
int main() {
int opcao = 2;
switch (opcao) {
case 1:
int saldo = 150;
std::cout << "Saldo: " << saldo << "\n";
break;
case 2:
std::cout << "Depósito\n";
break;
}
}
$ g++ -std=c++23 -Wall -Wextra declarar.cpp -o declarar
declarar.cpp: In function ‘int main()’:
declarar.cpp:10:14: error: jump to case label
10 | case 2:
| ^
declarar.cpp:7:17: note: crosses initialization of ‘int saldo’
7 | int saldo = 150;
| ^~~~~
- error: jump to case label
- "Salto para um rótulo de case". O problema está no salto para o
case 2. - note: crosses initialization of ‘int saldo’
- "Atravessa a inicialização de
saldo".
O motivo: o corpo do switch é um bloco só, e os case são apenas pontos de entrada nele. A variável saldo existe do ponto em que foi criada até a } do switch, o que inclui o case 2. Quando a opção é 2, o programa pula direto para o case 2, por cima da linha int saldo = 150;: saldo existiria ali, mas sem valor. O C++ proíbe esse salto.
Conserto: coloque o código do case dentro de chaves próprias. Assim, saldo só existe dentro delas:
#include <iostream>
int main() {
int opcao = 2;
switch (opcao) {
case 1: {
int saldo = 150;
std::cout << "Saldo: " << saldo << "\n";
break;
}
case 2:
std::cout << "Depósito\n";
break;
}
}
$ g++ -std=c++23 -Wall -Wextra declarar2.cpp -o declarar2 $ ./declarar2 Depósito
No editor do guia, o Clang diz o mesmo com outras palavras: error: cannot jump from switch statement to this case label ("não é possível pular do switch para este rótulo") e note: jump bypasses variable initialization ("o salto passa por cima da inicialização de uma variável").
Formas diferentes + qual usar
| Situação | Use | Por quê |
|---|---|---|
Uma variável inteira, char ou enum comparada com valores exatos (menu, opção, letra) | switch | Mais legível; com enum, o compilador confere se faltou um caso. |
Faixas (nota >= 7), double | else if | O switch só compara igualdade, e não aceita double. |
Textos (std::string) | else if com == | O switch não aceita texto. |
| Valores conhecidos só ao rodar | else if | Os case precisam de constantes. |
Condições variadas, com && e || | else if | Cada case só testa "é igual a este valor?". |
case 1 ... 5:?
Você pode encontrar na internet case 1 ... 5: (uma faixa de valores). Isso não é C++ padrão: é uma extensão do g++ e do Clang, que não funciona em outros compiladores, como o Visual Studio. Com a opção -Wpedantic (que avisa sobre tudo o que foge do padrão), o g++ denuncia: warning: range expressions in switch statements are non-standard [-Wpedantic] ("faixas em switch não são padrão"). Para faixas, use else if.
Quando dá errado
warning: this statement may fall through [-Wimplicit-fallthrough=]
O que significa: um case com código termina sem break, e o programa vai continuar no case de baixo.
Como consertar: se foi esquecimento (quase sempre), ponha o break;. Se a queda é proposital, escreva [[fallthrough]]; antes do próximo case.
error: switch quantity not an integer
O que significa: a variável do switch é std::string, double ou outro tipo não inteiro.
Como consertar: use if/else if. Se for um texto de uma letra só, use um char.
error: the value of ‘x’ is not usable in a constant expression
O que significa: um case usa uma variável comum.
Como consertar: torne-a const (se o valor é fixo) ou troque o switch por if.
error: duplicate case value
$ g++ -std=c++23 -Wall -Wextra duplicado.cpp -o duplicado
duplicado.cpp: In function ‘int main()’:
duplicado.cpp:9:9: error: duplicate case value
9 | case 1:
| ^~~~
duplicado.cpp:6:9: note: previously used here
6 | case 1:
| ^~~~
O que significa: "valor de case duplicado"; a nota mostra onde ele já tinha sido usado ("usado antes aqui"). Normalmente é um erro de copiar e colar.
Como consertar: cada valor só pode aparecer em um case. Corrija o número ou junte os dois blocos.
error: jump to case label + crosses initialization of …
O que significa: uma variável foi criada dentro de um case, sem chaves, e um case abaixo dela "pularia" a criação.
Como consertar: envolva o código do case em { … }, com o break dentro.
warning: enumeration value ‘…’ not handled in switch [-Wswitch]
O que significa: um valor do enum ficou sem case.
Como consertar: acrescente o case que falta. Evite pôr um default só para calar o aviso: você perde a conferência quando o enum ganhar valores novos.
Referência rápida
| Escrita | O que faz |
|---|---|
switch (x) { … } | Pula para o case com o valor de x (inteiro, char, bool ou enum) |
case 3: | Ponto de entrada para o valor 3 (constante, termina com :) |
break; | Sai do switch |
default: | Para os valores sem case |
case 'S': case 's': | Dois valores, mesmo código |
[[fallthrough]]; | Queda proposital para o próximo case (C++17) |
case 1: { int v = …; break; } | Variável própria do case |
switch (int v = f(); v) { … } | switch com inicializador (C++17), como o do if |
Exercícios
1. Complete o switch
Complete as palavras que faltam:
2. Com x = 2, o que mostra: switch (x) { case 1: std::cout << "a"; case 2: std::cout << "b"; case 3: std::cout << "c"; break; default: std::cout << "d"; }?
3. Qual destas variáveis não pode ir num switch?
4. Como consertar o erro jump to case label?
Missão final: a calculadora
Missão: a calculadora lê uma conta como 8 * 5 (a caixa já digita isso para você) e só sabe somar. Acrescente os casos '-', '*' e '/', cada um com seu break. Com 8 * 5, deve aparecer Resultado: 40. Bônus: faça a letra 'x' também multiplicar, juntando dois case.
#include <iostream>
int main() {
double a = 0, b = 0;
char op = ' ';
std::cin >> a >> op >> b;
switch (op) {
case '+':
std::cout << "Resultado: " << a + b << "\n";
break;
default:
std::cout << "Operação inválida: " << op << "\n";
}
}
#include <iostream>
int main() {
double a = 0, b = 0;
char op = ' ';
std::cin >> a >> op >> b;
switch (op) {
case '+':
std::cout << "Resultado: " << a + b << "\n";
break;
case '-':
std::cout << "Resultado: " << a - b << "\n";
break;
case '*':
case 'x':
std::cout << "Resultado: " << a * b << "\n";
break;
case '/':
std::cout << "Resultado: " << a / b << "\n";
break;
default:
std::cout << "Operação inválida: " << op << "\n";
}
}
$ ./calc 8 x 5 « no terminal, a Ana testou o bônus Resultado: 40 $ ./calc 9 / 2 Resultado: 4.5 $ ./calc 9 % 2 Operação inválida: %
Resumo
switch (x)pula direto para ocasecom o valor dex; odefaultpega o resto.- O
casesó marca onde começar. Quem para é obreak; sem ele, o programa cai no próximo caso (-Wimplicit-fallthrough, ligado pelo-Wextrano g++). - Rótulos colados (
case 'S': case 's':) dividem o mesmo código. Queda proposital com código:[[fallthrough]];. - Só inteiros,
char,booleenum; oscaseprecisam de constantes.std::stringedoubledãoswitch quantity not an integer. - Variável dentro de um
case: use chaves, ou vemjump to case label. - Faixas, textos e condições compostas:
else if.