Un valore, tante costanti
Una catena di else if che continua a confrontare la stessa variabile con delle costanti è uno schema che in C ha un'istruzione dedicata:
if (choice == 1) { /* ... */ }
else if (choice == 2) { /* ... */ }
else if (choice == 3) { /* ... */ }
else { /* ... */ }
switch lo dice in modo più diretto. Valuta l'espressione una volta sola, poi salta direttamente all'etichetta corrispondente.
Cambia choice ed eseguilo di nuovo. La struttura rende visibile a colpo d'occhio l'insieme dei valori ammessi, ed è questo il vero motivo per preferirla qui.
Sintassi e regole sulle etichette case
switch (espressioneIntera) {
case COSTANTE_1:
istruzioni
break;
case COSTANTE_2:
istruzioni
break;
default:
istruzioni
break;
}
Quattro vincoli che il C impone:
- L'espressione dello switch deve avere un tipo intero:
int,char,short,longo unenum. Nonfloat, nondoublee non una stringa. - Ogni etichetta
casedeve essere un'espressione costante nota in compilazione.case n:connvariabile è un errore;case 3 + 4:ecase MAX:(un#defineo una costante di enum) vanno bene. - Due case non possono avere lo stesso valore. I duplicati sono un errore di compilazione, una rete di sicurezza piccola ma reale.
- Un case non può esprimere un intervallo.
case 1 ... 5:è un'estensione di GCC, non C standard; per gli intervalli usa unif.
default è facoltativo e può comparire in qualsiasi punto del blocco, anche se metterlo per ultimo è una convenzione quasi universale. Senza default, un valore che non corrisponde a nulla salta semplicemente l'intero switch.
break: perché non è facoltativo
La cosa più sorprendente dello switch in C è che le etichette case sono destinazioni di salto, non scatole. Una volta che l'esecuzione atterra su un'etichetta, prosegue attraverso tutte le istruzioni sottostanti, oltrepassando le altre etichette case, finché non trova un break o la graffa di chiusura.
Il primo switch stampa uno, due e tre: ha trovato la corrispondenza con case 1 e poi ha eseguito tutto ciò che viene dopo. Il secondo stampa solo uno. Questo comportamento si chiama fall-through, e dimenticare un break è il classico bug dello switch in C: il programma è sbagliato in silenzio invece di non compilare.
Compila con gcc -Wall -Wextra -Wimplicit-fallthrough e il compilatore indica ogni case che prosegue nel successivo.
Il break sull'ultimo case è tecnicamente superfluo, perché il blocco finisce comunque lì. Scrivilo lo stesso, così che aggiungere in seguito un nuovo case sotto non crei di nascosto un fall-through.
Fall-through voluto
Il fall-through è una funzionalità quando è intenzionale. Impilare le etichette senza istruzioni in mezzo è il modo normale per dire "questi valori fanno la stessa cosa":
Tre etichette condividono un unico corpo, senza codice duplicato e senza una lunga condizione con ||. I compilatori non avvisano per questa forma, perché non ci sono istruzioni in cui cadere.
Il fall-through con istruzioni in mezzo, dove il case 1 viene eseguito e poi prosegue volutamente nel case 2, è più raro e merita un commento esplicito, perché chi legge deve sapere che il break mancante è una scelta:
switch (level) {
case 3:
printf("dettagliato\n");
/* fall through */
case 2:
printf("info\n");
/* fall through */
case 1:
printf("errori\n");
break;
}
/* fall through */ è il commento riconosciuto dall'avviso di GCC, quindi silenzia anche la diagnostica.
Switch sui caratteri
Dato che char è un tipo intero, la classificazione dei caratteri è un caso d'uso naturale:
Nota che case 'a': usa gli apici singoli: è una costante carattere, cioè semplicemente il valore intero di quel carattere. case "a": sarebbe una stringa e non compila.
Switch sugli enum
L'accoppiata migliore in C è switch con un enum. Le etichette case diventano nomi leggibili, e i compilatori possono avvisare quando lo switch non gestisce tutti gli enumeratori:
Due cose da copiare. Ogni case fa return, quindi non serve break: un return esce del tutto dalla funzione, e questo chiude anche lo switch. E non c'è default: con -Wswitch (incluso in -Wall), omettere default fa sì che il compilatore avvisi il giorno in cui qualcuno aggiunge un quinto stato e si dimentica di questa funzione. Un default l'avrebbe nascosto.
Dichiarare variabili dentro uno switch
Il corpo di uno switch è un unico blocco, quindi una dichiarazione in un case è visibile, anche se non necessariamente inizializzata, in quelli successivi. Saltare un'inizializzazione è un errore in C:
switch (n) {
case 1:
int x = 10; // errore in C: un'etichetta non puo precedere cosi una dichiarazione
printf("%d\n", x);
break;
case 2:
printf("%d\n", x); // x esiste qui, ma non e mai stata inizializzata
break;
}
Racchiudi il case tra graffe proprie quando ha bisogno di variabili locali:
switch (n) {
case 1: {
int x = 10;
printf("%d\n", x);
break;
}
case 2:
printf("due\n");
break;
}
switch o if else?
Usa switch quando è vero tutto questo: stai controllando una sola espressione, ha un tipo intero e la confronti con costanti fisse. Gestione dei menu, macchine a stati con enum, classificazione di caratteri e smistamento di comandi sono i casi tipici. Il guadagno è la leggibilità, il controllo dei valori duplicati e un compilatore che può avvisarti degli enumeratori non gestiti.
Usa if else quando i controlli riguardano intervalli (score >= 90), valori in virgola mobile, stringhe (ti serve strcmp), più variabili diverse o condizioni combinate con && e ||. Nessuna di queste cose si può esprimere come etichetta case.
Le prestazioni raramente sono il fattore decisivo. I compilatori trasformano uno switch denso in una jump table e uno sparso in una serie di confronti, quindi con pochi case le due forme sono indistinguibili. Scegli quella che esprime l'intenzione.
Domande frequenti
Come funziona l'istruzione switch in C?
switch (expr) valuta una volta un'espressione intera, salta all'etichetta case la cui costante corrisponde ed esegue da lì finché non incontra un break o la fine dello switch. Se non corrisponde nulla, salta a default se è presente, altrimenti salta l'intero blocco.
Perché serve break in uno switch in C?
Perché le etichette case sono destinazioni di salto, non blocchi separati. Senza break, l'esecuzione prosegue dritta nelle istruzioni del case successivo: si chiama fall-through. Un break dimenticato è il bug più comune con lo switch in C, e gcc -Wimplicit-fallthrough lo segnala.
Si può fare uno switch su una stringa in C?
No. Uno switch in C richiede un'espressione intera, quindi i valori int, char ed enum funzionano, ma stringhe, numeri in virgola mobile e intervalli no. Per scegliere un ramo in base a una stringa, usa strcmp in una catena if else, oppure converti prima la stringa in un enum e fai lo switch su quello.
Quando usare switch invece di if else in C?
Usa switch quando confronti un intero o un carattere con un elenco di costanti fisse: comandi di un menu, stati di un enum, classificazione di caratteri. Usa una catena if else quando i controlli riguardano intervalli, più variabili o qualsiasi cosa che non sia un'uguaglianza con una costante nota in compilazione.