Istruzione Switch in Linguaggio C

L'istruzione switch in linguaggio C permette di trasferire il controllo di flusso ad una o più istruzioni scegliendo tra differenti alternative a seconda del valore che assume l'espressione di controllo.

Un'istruzione switch è più conveniente da utilizzare rispetto ad un'istruzione if quando il numero di casi da dover gestire è elevato, per cui risulta macchinoso utilizzare tante istruzioni if concatenate.

In linguaggio C, tuttavia, l'istruzione switch ha dei limiti in quanto può essere usata solo con espressioni di controllo a valori interi.

In questa lezione vedremo nel dettaglio come usare queste istruzioni per controllare il flusso dei nostri programmi.

Concetti Chiave
  • Le istruzioni switch in linguaggio C sono istruzioni condizionali che permettono di saltare ad una istruzione scegliendo tra varie alternative in base al valore dell'espressione di controllo;
  • L'espressione di controllo deve essere a valori interi;
  • I vari casi devono essere specificati attraverso le clausole case;
  • Ogni gruppo di istruzioni appartenenti ad un caso devono terminare con un'istruzione break;
  • In assenza di break viene eseguito il caso successivo;
  • Il caso di default va specificato con la clausola default.

Istruzioni switch

Nello sviluppo di programmi sovente capita di dover comparare un'espressione con una serie di valori per scoprire a quale di questi essa corrisponda. Nella lezione sulle istruzioni if abbiamo visto che è possibile usare a questo scopo le istruzioni if concatenate.

Per esempio, supponiamo di avere una variabile intera che contenga un valore associato al giorno della settimana. Questa variabile può assumere un valore che va da 1, ossia Lunedì, a 7, ossia Domenica. Vogliamo stampare il giorno corrispondente al suo valore, oppure stampare Giorno non valido nel caso in cui il valore non sia compreso tra 1 e 7.

Per far questo, possiamo scrivere il codice seguente usando l'istruzione if:

if (giorno_settimana == 1)
    printf("Lunedì");
else if (giorno_settimana == 2)
    printf("Martedì");
else if (giorno_settimana == 3)
    printf("Mercoledì");
else if (giorno_settimana == 4)
    printf("Giovedì");
else if (giorno_settimana == 5)
    printf("Venerdì");
else if (giorno_settimana == 6)
    printf("Sabato");
else if (giorno_settimana == 7)
    printf("Domenica");
else
    printf("Giorno non valido");

Tuttavia, il linguaggio C mette a disposizione un'istruzione che semplifica la gestione di questi casi: l'istruzione switch.

Utilizzando questa istruzione possiamo riscrivere il codice di sopra in questo modo:

switch (giorno_settimana) {
    case 1:     printf("Lunedì");
                break;
    case 2:     printf("Martedì");
                break;
    case 3:     printf("Mercoledì");
                break;
    case 4:     printf("Giovedì");
                break;
    case 5:     printf("Venerdì");
                break;
    case 6:     printf("Sabato");
                break;
    case 7:     printf("Domenica");
                break;
    default:    printf("Giorno non valido!");
                break;
}

Quando l'istruzione switch, in cima al frammento di codice dell'esempio, viene eseguita, il valore della variabile giorno_settimana viene comparato con i valori da 1 a 7.

Se tale valore, ad esempio, risulta essere 3 allora viene eseguita l'istruzione printf("Mercoledì"), quindi viene stampato il messaggio Mercoledì e poi viene eseguita l'istruzione break. L'istruzione break trasferisce il controllo di esecuzione all'istruzione che segue l'istruzione switch.

Nel caso in cui il valore della variabile giorno_settimana non corrisponde a nessuna scelta della lista, viene eseguito il caso default, per cui viene stampato il messaggio Giorno non valido.

Il vantaggio di usare un'istruzione switch sta nel fatto che risulta molto più leggibile rispetto ad una serie di istruzioni if concatenate. Inoltre, in molti casi il codice prodotto dal compilatore per la gestione degli switch è molto più efficiente rispetto al caso di istruzioni if concatenate.

Nella sua forma più comune l'istruzione switch ha la forma seguente:

switch ( espressione_intera ) {
    case espressione_costante_1:    istruzioni;
                                    break;

    case espressione_costante_2:    istruzioni;
                                    break;
    /* .... */
    default:                        istruzioni;
                                    break;
}

Dal momento che un'istruzione switch è abbastanza complessa esaminiamone i componenti uno ad uno.

  • Espressione intera di controllo. La parola chiave switch deve essere seguita immediatamente da un'espressione di controllo tra parentesi. Tale espressione deve essere di tipo intera o, comunque, riconducibile ad un intero. Ad esempio, espressioni tra interi sono supportate e valori char vengono convertiti in interi. Non sono supportati, invece, valori floating point o stringhe.

  • Etichette dei casi. Ciascun caso inizia con un'etichetta della forma:

    case espressione_costante:
    

    L'espressione espressione_costante è una qualunque espressione intera ordinaria dove, però, non sono ammesse variabili o chiamate a funzione.

    Per cui, ad esempio l'espressione 6 è un'espressione costante ed è ammessa.

    Analogamente anche le espressioni 2 * 4 oppure 20 - 2 sono ammesse in quanto espressioni costanti. Non sono espressioni costanti espressioni del tipo x * 2 oppure y - 4 dove compaiono variabili.

    Inoltre, le espressioni devono risultare in un valore intero.

  • Istruzioni. Ogni etichetta dei casi può essere seguita da un numero arbitrario di istruzioni senza la necessità di utilizzare parentesi graffe. Tipicamente, l'ultima istruzione in ciascun gruppo è un'istruzione break.

Definizione

Istruzione switch

Un'istruzione switch è un'istruzione condizionale che permette di eseguire istruzioni differenti a seconda del valore assunto da un'espressione di controllo a valori interi.

La sintassi generale di un'istruzione switch è la seguente:

switch ( espressione_di_controllo ) {
    case espressione_costante_1:    istruzioni;
                                    break;

    case espressione_costante_2:    istruzioni;
                                    break;
    /* .... */
    default:                        istruzioni;
                                    break;
}
  • L'espressione espressione_di_controllo deve essere a valori interi;
  • Le varie espressioni_costanti devono avere valori interi;
  • L'etichetta default viene eseguita nel caso in cui l'espressione di controllo non corrisponda a nessun valore;
  • Ciascuna istruzione dei casi deve essere seguita da un'istruzione break.

Avvertenze nell'uso dell'istruzione switch

Nell'adoperare l'istruzione switch all'interno dei programmi scritti in linguaggio C bisogna prestare attenzione.

Nota

I casi non possono essere duplicati.

I casi presenti all'interno di un'istruzione switch non possono essere duplicati. Non è possibile scrivere un codice del genere:

/* Codice non valido */
switch (variabile) {
    case 1:     istruzione;
                break;
    case 1:     istruzione;
                break;
    default:    istruzione;
                break;
}
Consiglio

L'ordine dei casi è ininfluente.

L'ordine con cui appaiono i casi nell'istruzione switch è totalmente ininfluente. Non è necessario seguire un ordine anche perché, semplicemente, il compilatore selezionerà sempre il caso corrispondente al valore dell'espressione di controllo.

Consiglio

Il caso default non deve essere necessariamente l'ultimo.

Come conseguenza dell'osservazione precedente risulta che il caso default non deve apparire per ultimo. Possiamo, infatti, scrivere un codice di questo tipo:

switch (variabile) {
    default:    istruzione;
                break;
    case 1:     istruzione;
                break;
    /* ... */
}
Consiglio

Il caso default non è obbligatorio.

Il caso di default in un'istruzione switch non è obbligatorio. In caso di assenza, se il valore dell'espressione di controllo non combacia con nessuno dei casi, il controllo semplicemente passa all'istruzione successiva all'istruzione switch.

Ad esempio:

switch (x) {
    case 1:     istruzione_1;
                break;
    case 2:     istruzione_2;
                break;
}

istruzione_successiva;

Se la variabile x non vale 1 o 2 semplicemente il controllo passa all'istruzione istruzione_successiva.

Casi multipli

In generale, per le istruzioni switch vale la seguente regola:

Nota

La parola chiave case può essere seguita da una sola espressione costante.

Non è possibile associare allo stesso caso più espressioni costanti. Ad esempio, il seguente codice non è valido:

switch(x) {
    /* CODICE NON VALIDO! */
    case 1, 2, 3:   istruzione;
                    break;
    /* ... */
}

Inoltre, a differenza di altri linguaggi di programmazione, non è possibile specificare un'intervallo per un caso dell'istruzione switch.

La limitazione di sopra, tuttavia, può essere aggirata in un certo modo. Infatti, omettendo l'istruzione break più casi possono precedere lo stesso insieme di istruzioni.

Ad esempio, riprendendo l'esempio di prima dei giorni della settimana, vogliamo scrivere del codice che stampa a schermo se un giorno è feriale o festivo.

Ingenuamente, potremmo scrivere un codice di questo tipo:

switch (giorno_settimana) {
    case 1:     printf("Feriale");
                break;
    case 2:     printf("Feriale");
                break;
    case 3:     printf("Feriale");
                break;
    case 4:     printf("Feriale");
                break;
    case 5:     printf("Feriale");
                break;
    case 6:     printf("Festivo");
                break;
    case 7:     printf("Festivo");
                break;
    default:    printf("Giorno non valido!");
                break;
}

Tuttavia, poiché i casi 1, 2, 3, 4 e 5 e i casi 6 e 7 sono uguali tra di loro, possiamo riscrivere in maniera più sintetica il codice, evitando di usare break, in questo modo:

switch (giorno_settimana) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:     printf("Feriale");
                break;
    case 6:
    case 7:     printf("Festivo");
                break;
    default:    printf("Giorno non valido!");
                break;
}

Per cui:

Definizione

Casi multipli per un'istruzione switch

Per gestire casi multipli in un'istruzione switch in linguaggio C è sufficiente far precedere il gruppo di istruzioni comuni dalle varie etichette dei casi omettendo l'istruzione break se non nell'ultima istruzione.

La forma dei casi multipli è la seguente:

switch (espressione_di_controllo) {
    /* ... */

    case caso_1:
    case caso_2:
    /* ... */
    case caso_n:    istruzione_1;
                    istruzione_2;
                    /* ... */
                    istruzione_n;
                    break;

    /* ... */
}

Istruzione break

A questo punto che abbiamo visto in dettaglio l'istruzione switch ed il suo funzionamento, risulta necessario soffermarsi un secondo sull'istruzione break e comprendere a fondo che cosa faccia esattamente.

Da quanto abbiamo visto sopra, lo scopo dell'istruzione break è quello di uscire fuori dall'istruzione switch e continuare l'esecuzione dall'istruzione successiva.

Nella realtà, l'istruzione switch non è altro che un salto calcolato per cui la presenza dell'istruzione switch è necessaria. Infatti, quando viene valutata l'espressione di controllo, il controllo del programma salta all'etichetta case corrispondente. Un'etichetta case non è altro, alla fine, che un segnaposto che indica una posizione all'interno dello switch. Quando l'ultima istruzione del caso è stata eseguita, il controllo continua alla prossima istruzione del caso successivo e l'etichetta del caso successivo viene ignorata. Se non usassimo l'istruzione break il controllo passerebbe da un caso all'altro.

Per chiarire il tutto, ritorniamo all'esempio dei giorni della settimana:

#include <stdio.h>

int main() {
    int giorno_settimana = 1;

    switch (giorno_settimana) {
        case 1:     printf("Lunedì\n");
                    break;
        case 2:     printf("Martedì\n");
                    break;
        case 3:     printf("Mercoledì\n");
                    break;
        case 4:     printf("Giovedì\n");
                    break;
        case 5:     printf("Venerdì\n");
                    break;
        case 6:     printf("Sabato\n");
                    break;
        case 7:     printf("Domenica\n");
                    break;
        default:    printf("Giorno non valido!\n");
                    break;
    }

    return 0;
}

Se proviamo a compilare questo programma e lo eseguiamo, l'output che otteniamo è quello corretto:

Lunedì

Adesso, proviamo a rimuovere tutte le istruzioni break in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main() {
    int giorno_settimana = 1;

    switch (giorno_settimana) {
        case 1:     printf("Lunedì\n");
        case 2:     printf("Martedì\n");
        case 3:     printf("Mercoledì\n");
        case 4:     printf("Giovedì\n");
        case 5:     printf("Venerdì\n");
        case 6:     printf("Sabato\n");
        case 7:     printf("Domenica\n");
        default:    printf("Giorno non valido!\n");
    }

    return 0;
}

Se compiliamo ed eseguiamo il programma di sopra, otteniamo il seguente output:

Lunedì
Martedì
Mercoledì
Giovedì
Venerdì
Sabato
Domenica
Giorno non valido!

Questo accade perché l'istruzione switch controlla il valore della variabile giorno_settimana e, constatando che si tratta del valore 1, salta alla riga 7. A questo punto, il programma stampa correttamente la stringa "Lunedì\n" attraverso la funzione printf. Successivamente, non trovando un'istruzione break, il programma passa alla riga 8, poi alla riga 9 e così via, scorrendo tutti i casi dell'istruzione switch.

Nota

Dimenticare break alla fine di ogni caso è un errore tipico.

Uno degli errori più comuni di programmazione in linguaggio C è quello di dimenticare di aggiungere l'istruzione break al termine di ogni caso. Anche se, come abbiamo visto per i casi multipli, esistono situazioni in cui questa scelta è deliberata.

Per evitare errori conviene sempre aggiungere l'istruzione break a tutti i casi. Conviene, anzi, aggiungere break anche all'ultimo caso anche se questo non è strettamente necessario:

switch (espressione_di_controllo) {
    case caso_1:    istruzione;
                    break;
    case caso_2:    istruzione;
                    break;
    /* ... */
    default:        istruzione;
                    /* Non necessario */
                    /* Ma è buona norma */
                    break;
}

Nel caso in cui volontariamente bisogna omettere l'istruzione break si può utilizzare un commento per indicare che si tratta di una situazione voluta:

switch (espressione_di_controllo) {
    case caso_1:    /* Caso Multiplo */
    case caso_2:    istruzione;
                    break;
    /* ... */
    default:        istruzione;
                    break;
}

In Sintesi

In questa lezione abbiamo studiato la seconda istruzione condizionale messa a disposizione dal linguaggio C: l'istruzione switch. Rispetto all'istruzione if, l'istruzione switch permette di definire le operazioni da effettuare quando le condizioni da verificare sono più di un semplice vero o falso.

La limitazione di questa istruzione, tuttavia, sta nel fatto che la condizione di controllo deve avere un valore di tipo intero ed i casi possono essere singoli valori costanti di tipo intero.