- Programmazione » Programmazione » Guida C - Manuale programmazione con articoli e risorse interessanti
Il flusso elaborativo: switch
switch
La if gestisce ottimamente quelle situazioni in cui,
a seguito della valutazione di una condizione, si presentano
due sole possibilità alternative. Quando le
alternative siano più di due, si è costretti a
utilizzare più istruzioni if nidificate, il
che può ingarbugliare non poco la struttura logica del
codice e menomarne la leggibilità.
Quando la condizione da valutare sia esprimibile mediante
un'espressione restituente un int o un
char, il C rende disponibile l'istruzione
switch, che consente di valutare un numero qualsiasi
di alternative per il risultato di detta espressione. Diamo
subito un'occhiata ad un caso pratico:
#define EOF -1
#define LF 10
#define CR 13
#define BLANK ' '
....
char c;
long ln = 0L, cCount = 0L;
....
switch(c = fgetc(inFile)) {
case EOF:
return;
case LF:
if(++ln == MaxLineNum)
return;
case BLANK:
cCount++;
case NULL:
case CR:
break;
default:
*ptr++ = c;
}
Il frammento di codice riportato fa parte di una funzione che
legge il contenuto di un file carattere per carattere ed
esegue azioni diverse a seconda del carattere letto: in
particolare, la funzione fgetc() legge un carattere
dal file associato al descrittore[2] inFile e lo
restituisce. Tale carattere è memorizzato nella
variabile c, dichiarata di tipo char.
L'operazione di assegnamento è, in C,
un'espressione che restituisce il valore assegnato,
pertanto il valore memorizzato nella variabile c
è valutato dalla switch, che esegue una delle
possibili alternative definite. Se si tratta del valore
definito dalla costante manifesta EOF la funzione
termina; se si tratta del carattere definito come LF
viene valutato quante righe sono già state scandite
per decidere se terminare o no; se si tratta di un
LF o di un BLANK è incrementato un
contatore; i caratteri definiti come CR e
NULL (il solito zero binario) vengono semplicemente
ignorati; qualsiasi altro carattere è copiato in un
buffer il cui puntatore è incrementato di
conseguenza.
E' meglio scendere in maggiori dettagli. Per prima cosa
va osservato che l'espressione da valutare deve trovarsi
tra parentesi tonde. Inoltre il corpo della switch,
cioè l'insieme delle alternative, è
racchiuso tra parentesi graffe. Ogni singola alternativa
è definita dalla parola chiave case, seguita
da una costante (non sono ammesse variabili o espressioni non
costanti) intera (o char), a sua volta seguita dai
due punti (":"). Tutto ciò che
segue i due punti è il codice che viene eseguito
qualora l'espressione valutata assuma proprio il valore
della costante tra la case e i due punti, fino alla
prima istruzione break incontrata (se incontrata!),
la quale determina l'uscita dalla switch,
cioè un salto alla prima istruzione che segue la
graffa chiusa. La parola chiave default seguita dai
due punti introduce la sezione di codice da eseguire qualora
l'espressione non assuma nessuno dei valori specificati
dalle diverse case. Ma non finisce qui.
Tra le parentesi graffe deve essere specificata almeno una
condizione: significa che la switch potrebbe essere
seguita anche da una sola case o dalla
default, e che quindi possono esistere delle
switch prive di default o di case.
La default, comunque, se presente è unica.
Complicato? Più a parole che nei fatti...
Torniamo all'esempio: cosa accade se c vale
EOF? viene eseguito tutto ciò che segue i due
punti, cioè l'istruzione return. Questa
ci "catapulta" addirittura fuori dalla funzione
eseguita in quel momento, quindi della switch non
siparla proprio più...
Se invece c vale LF, l'esecuzione
salta alla if che segue immediatamente la seconda
case. Se la condizione valutata dalla if
è vera... addio funzione; altrimenti l'esecuzione
prosegue con l'istruzione immediatamente successiva.
E' molto importante sottolineare che, a differenza di
quanto si potrebbe pensare, la presenza di altre
case non arresta l'esecuzione e non produce
l'uscita dalla switch: viene quindi incrementata
la variabile cCount. Solo a questo punto
l'istruzione break determina l'uscita dalla
switch.
L'incremento della cCount è invece la
prima istruzione eseguita se c vale BLANK,
ed è anche... l'ultima perché subito dopo
si incontra la break. Se c vale CR
o NULL si incontra immediatamente la break,
e quindi si esce subito dalla switch. Da ciò
si vede che quando in una switch è necessario
trattare due possibili casi in modo identico è
sufficiente accodare le due case. Infine, se in
c non vi è nessuno dei caratteri esaminati,
viene eseguito ciò che segue la
default.
E' forse superfluo precisare che le break, se
necessario, possono essere più di una e possono
dipendere da altre condizioni valutate all'interno di una
case, ad esempio mediante una if. Inoltre
una case può contenere un'intera
switch, nella quale ne può essere annidata
una terza... tutto sta a non perdere il filo logico dei
controlli. Esempio veloce:
switch(a) {
case 0:
switch(b) {
case 25:
....
break;
case 30:
case 31:
....
case 40:
....
break;
default:
....
}
....
break;
case 1:
....
break;
case 2:
....
}
Se a è uguale a 0 viene eseguita la
seconda switch, al termine della quale si rientra
nella prima (e sempre nella parte di codice dipendente dalla
case per 0). La prima switch,
inoltre, non ha la default: se a non vale
0, né 1, né 2
l'esecuzione salta direttamente alla prima istruzione che
segue la graffa che la chiude.
I blocchi di istruzioni dipendenti da una case,
negli esempi visti, non sono mai compresi tra graffe. In
effetti esse non sono necessarie (ma lo sono, ripetiamolo,
per aprire e chiudere la switch), però, se
presenti, non guastano. In una parola: sono facoltative.