Il costrutto switch in C e C++

In molti problemi di programmazione ci troviamo di fronte a scelte multiple: in base al valore di una variabile, il programma deve eseguire azioni differenti.
Una soluzione è utilizzare una serie di if-else-if in cascata, ma questa può rendere il codice poco leggibile.
Per queste situazioni, in C e C++ si utilizza l’istruzione di selezione multipla switch.

Sintassi generale

switch (espressione) {
   case valore1:
       // istruzioni
       break;
   case valore2:
       // istruzioni
       break;
   ...
   default:
       // istruzioni di default
}

Osservazioni

  1. L’espressione può restituire solo valori interi o carattere.
  2. Ogni case deve avere una costante intera o char univoca.
  3. Ogni ramo termina tipicamente con break per evitare il fall-through (esecuzione dei case successivi).
  4. Il blocco default è opzionale, eseguito quando non c’è alcuna corrispondenza.

Esempio 1 – Menù bancomat

#include <iostream>
using namespace std;

void prelievo(){ cout << "Hai scelto il prelievo.\n"; }
void lista(){ cout << "Hai scelto la lista movimenti.\n"; }
void saldo(){ cout << "Hai scelto il saldo.\n"; }

int main() {
    char ch;
    cout << "1. Prelievo\n";
    cout << "2. Lista movimenti\n";
    cout << "3. Saldo\n";
    cin >> ch;

    switch(ch) {
        case '1': prelievo(); break;
        case '2': lista(); break;
        case '3': saldo(); break;
        default: cout << "Nessuna opzione selezionata\n";
    }
    return 0;
}

Esempio 2 – Giorno della settimana

#include <iostream>
using namespace std;

int main() {
    int giorno;
    cout << "Inserisci un numero (1..7): ";
    cin >> giorno;

    switch(giorno) {
        case 1: cout << "Lunedi'" << endl; break;
        case 2: cout << "Martedi'" << endl; break;
        case 3: cout << "Mercoledi'" << endl; break;
        case 4: cout << "Giovedi'" << endl; break;
        case 5: cout << "Venerdi'" << endl; break;
        case 6: cout << "Sabato" << endl; break;
        case 7: cout << "Domenica" << endl; break;
        default: cout << "Valore non valido" << endl;
    }
    return 0;
}

Uso del fall-through

Una volta acquisito il valore intero della variabile giorno, il controllo passa al case corrispondente.
Se, ad esempio, inseriamo il valore 4 e non mettiamo break, vengono eseguite anche le istruzioni dei case successivi (5, 6, 7, e anche default se presente).
Questo comportamento si chiama fall-through. Per limitarlo ai soli casi desiderati, si utilizza break in chiusura di ciascun ramo.
È anche possibile far confluire più case nello stesso blocco di istruzioni.

Attenzione: il fall-through (mancanza di break) è spesso un errore, ma a volte è voluto. Ecco quando può essere utile:

  • Raggruppare più case che condividono le stesse istruzioni (es.: mesi da 30 giorni).
  • Applicare logiche a “livelli” dove i casi successivi ereditano parte del comportamento dei precedenti.
  • Mappare categorie affini (es.: vocali vs consonanti) evitando duplicazioni.

Esempio (raggruppamento corretto):

switch(mese) {
  case 4: case 6: case 9: case 11:
    giorni = 30;
    break;
  case 1: case 3: case 5: case 7: case 8: case 10: case 12:
    giorni = 31;
    break;
  case 2:
    // logica febbraio...
    break;
  default:
    // non valido
}

Esempio (fall-through intenzionale, segnalato):

switch(livello) {
  case 3:
    bonus += 30;
    [[fallthrough]];   // C++17: indica che il fall-through è voluto
  case 2:
    bonus += 20;
    /* fallthrough */  // C / GCC/Clang: commento esplicito
  case 1:
    bonus += 10;
    break;
  default:
    bonus = 0;
}

Common pitfalls:

  • Dimenticare break quando non si intende il fall-through.
  • Valori duplicati nei case (non ammessi nello stesso switch).
  • Usare espressioni non costanti nei case (servono costanti note a compile-time).
  • Stringhe nei case (non supportate in C/C++; usare if-else-if o mappe).

Esempio 3 – Giorni del mese

#include <iostream>
using namespace std;

int main() {
    int mese, anno, giorniMese;
    cout << "Inserisci mese (1..12) e anno: ";
    cin >> mese >> anno;

    switch(mese) {
        case 1: case 3: case 5: case 7: case 8: case 10: case 12:
            giorniMese = 31; break;
        case 4: case 6: case 9: case 11:
            giorniMese = 30; break;
        case 2:
            if ( (anno % 4 == 0 && anno % 100 != 0) || (anno % 400 == 0) )
                giorniMese = 29;
            else
                giorniMese = 28;
            break;
        default:
            cout << "Mese non valido" << endl;
            return 0;
    }

    cout << "Il mese ha " << giorniMese << " giorni." << endl;
    return 0;
}
switch(n) {
    case 1:
    case 2:
        cout << "n vale 1 oppure 2\n";
        break;
    case 3:
        cout << "n vale 3\n";
        break;
}

Esercizi

Esercizio 1 – Completa il codice
switch(myVar) {
    case 1:
    case 2:
        cout << "myVar e' uguale a _______ ";
        break;
    case 3:
        cout << "myVar e' uguale a _______ ";
        break;
    case 4:
        cout << "myVar e' uguale a _______ ";
        break;
}
Esercizio 2 – Programma incompleto
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Inserisci un numero intero: ";
    cin >> n;

    switch(n) {
        case 1:
            cout << "Hai inserito il numero _______";
            break;
        case 0:
            cout << "Hai inserito il numero _______";
            break;
        // aggiungi altri case, se necessario
        default:
            cout << "Valore non gestito";
    }
    return 0;
}
Esercizio 3 – Mesi

Scrivi un programma che, dato un numero da 1 a 12, visualizzi il mese corrispondente (es. 1 → Gennaio, 2 → Febbraio, …).

Confronto: switch vs if-else-if

Caratteristica switch if-else-if
Tipi di confronto Solo uguaglianza con costanti intere o char Qualsiasi condizione logica (>, <, ==, &&, ||, …)
Leggibilità Più compatto e chiaro per scelte multiple Diventa poco leggibile con molte condizioni
Fall-through Presente di default (serve break) Non esiste: ogni ramo è indipendente
Flessibilità Limitato a costanti note a compile-time Può usare variabili, espressioni e funzioni complesse
Uso tipico Menù, mapping numeri → etichette, giorni, mesi… Decisioni basate su condizioni logiche articolate
Efficienza Spesso più efficiente: il compilatore può ottimizzare con jump table Generalmente meno efficiente se le condizioni sono numerose

In sintesi: switch è ideale per scelte multiple semplici e leggibili, mentre if-else-if rimane insostituibile quando occorrono condizioni complesse o confronti non basati su uguaglianze.

Ora tocca a te – Esercizi

Esercizio 1 – Completa il codice
switch(myVar) {
    case 1:
    case 2:
        cout << "myVar e' uguale a _______ ";
        break;
    case 3:
        cout << "myVar e' uguale a _______ ";
        break;
    case 4:
        cout << "myVar e' uguale a _______ ";
        break;
}
Esercizio 2 – Programma incompleto
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Inserisci un numero intero: ";
    cin >> n;

    switch(n) {
        case 1:
            cout << "Hai inserito il numero _______";
            break;
        case 0:
            cout << "Hai inserito il numero _______";
            break;
        // aggiungi altri case a piacere
    }
    return 0;
}
Esercizio 3 – Giorni della settimana

Scrivi un programma che, dato un numero da 1 a 7, visualizzi il giorno della settimana corrispondente (1 → Lunedì, …, 7 → Domenica).
Usa switch e il blocco default per i valori non validi.

Esercizio 4 – Menù calcolatrice

Realizza un menù testuale che offra all’utente 4 scelte:

1. Somma    2. Differenza    3. Prodotto    4. Divisione

Leggi due numeri e, in base alla scelta, esegui l’operazione corrispondente. Se l’utente inserisce un’opzione non valida, mostra un messaggio di errore.

Esercizio 5 – Vocale o consonante (fall-through)

Scrivi un programma che, dato un carattere alfabetico, determini se è vocale o consonante.
Usa uno switch con più case in sequenza per gestire le vocali (a, e, i, o, u in maiuscolo e minuscolo).
Sfrutta il fall-through per raggruppare più casi nello stesso blocco di istruzioni.

Soluzioni

Soluzione Esercizio 1 – Completa il codice

Le tre righe mancanti sono i valori visualizzati (1, 3, 4) coerenti con i rami case.

switch(myVar) {
  case 1:
  case 2:
    cout << "myVar e' uguale a 1 oppure 2";
    break;
  case 3:
    cout << "myVar e' uguale a 3";
    break;
  case 4:
    cout << "myVar e' uguale a 4";
    break;
}

Soluzione Esercizio 2 – Programma incompleto
#include <iostream>
using namespace std;

int main() {
  int n;
  cout << "Inserisci un numero intero: ";
  cin >> n;

  switch(n) {
    case 1:
      cout << "Hai inserito il numero 1";
      break;
    case 0:
      cout << "Hai inserito il numero 0";
      break;
    default:
      cout << "Valore non gestito";
  }
  return 0;
}

Soluzione Esercizio 3 – Giorni della settimana
#include <iostream>
using namespace std;

int main() {
  int giorno;
  cout << "Inserisci un numero (1..7): ";
  cin >> giorno;

  switch(giorno) {
    case 1: cout << "Lunedi'";    break;
    case 2: cout << "Martedi'";   break;
    case 3: cout << "Mercoledi'"; break;
    case 4: cout << "Giovedi'";   break;
    case 5: cout << "Venerdi'";   break;
    case 6: cout << "Sabato";     break;
    case 7: cout << "Domenica";   break;
    default: cout << "Valore non valido";
  }
  cout << endl;
  return 0;
}

Soluzione Esercizio 4 – Menù calcolatrice
#include <iostream>
using namespace std;

int main() {
  cout << "1) Somma\n2) Differenza\n3) Prodotto\n4) Divisione\n";
  cout << "Scegli opzione (1-4): ";
  int scelta; cin >> scelta;

  double a, b;
  cout << "Inserisci due numeri: ";
  cin >> a >> b;

  switch(scelta) {
    case 1:
      cout << "Somma = " << (a + b);
      break;
    case 2:
      cout << "Differenza = " << (a - b);
      break;
    case 3:
      cout << "Prodotto = " << (a * b);
      break;
    case 4:
      if (b == 0) cout << "Errore: divisione per zero!";
      else        cout << "Quoziente = " << (a / b);
      break;
    default:
      cout << "Opzione non valida";
  }
  cout << endl;
  return 0;
}

Soluzione Esercizio 5 – Vocale o consonante (fall-through)
#include <iostream>
#include <cctype>   // tolower
using namespace std;

int main() {
  char c;
  cout << "Inserisci una lettera: ";
  cin >> c;

  char ch = tolower(static_cast<unsigned char>(c));

  switch(ch) {
    case 'a': case 'e': case 'i': case 'o': case 'u':
      cout << c << " e' una vocale";
      break;
    default:
      if ( (ch >= 'a' && ch <= 'z') ) cout << c << " e' una consonante";
      else                            cout << c << " non e' una lettera";
  }
  cout << endl;
  return 0;
}

Le vocali sono gestite con più case in sequenza (fall-through intenzionale).