Capitolo 5

Strutture di controllo

Un programma è composto da una serie di istruzioni che possono essere eseguite nell’ordine in cui vengono scritte e in modo sequenziale, oppure in modo non lineare, mediante l’utilizzo di appositi costrutti sintattici di programmazione, definiti nel loro insieme strutture di controllo, che sono espressi mediante delle apposite keyword del linguaggio che rappresentano essi stessi delle istruzioni (Tabella 5.1).

Tabella 5.1 Classificazione delle strutture di controllo.
Istruzioni di selezione Istruzioni di iterazione Istruzioni di salto
if while break
switch do continue
for goto
return

NOTA

In C esistono anche altre tipologie di istruzioni, come le istruzioni composte (compound statement), rappresentate dai blocchi di codice delimitati dalle parentesi graffe { }; le istruzioni nulle (null statement), rappresentate dal singolo carattere punto e virgola; le istruzioni espressione (expression statement), rappresentate da un’espressione terminata dal carattere punto e virgola.

Espressioni, istruzioni e blocchi di codice: un breve ripasso

Un’espressione è un qualsiasi costrutto sintattico composto da un insieme di operatori e operandi in cui gli operandi rappresentano dei valori che sono valutati al fine di ritornare, generalmente, un altro valore come risultato. Gli operandi possono essere variabili, costanti, invocazioni di metodi, altre espressioni e così via, mentre gli operatori possono essere tutti quelli visti finora. Un’istruzione è, invece, definibile come un costrutto sintattico che consente di compiere determinate operazioni nell’ambito di un programma informatico. Possiamo infatti avere istruzioni di dichiarazione e di inizializzazione delle variabili, di selezione e di controllo del flusso esecutivo del programma e anche istruzioni che rappresentano esse stesse delle espressioni, come quando, per esempio, assegniamo una valore a una variabile. Un blocco di codice è, infine, un insieme di più istruzioni racchiuse tra le parentesi graffe { }.

Istruzioni di selezione

Le istruzioni di selezione (selection statement) consentono di dirigere e distribuire il flusso di esecuzione del codice verso determinate istruzioni piuttosto che verso altre in base al valore di un’espressione denominata espressione di controllo (controlling expression). L’espressione di controllo è altresì considerata una full expression, e pertanto qualsiasi eventuale effetto collaterale che possa lì prodursi sarà valutato prima di una successiva full expression.

Istruzione di selezione singola if

La prima e più semplice struttura di controllo è quella definita di selezione singola if (Sintassi 5.1 e 5.2), che permette di eseguire una o più istruzioni se, e solo se, una determinata espressione è vera.

Sintassi 5.1 Istruzione if che esegue una singola istruzione.

if (expression) statement;

La Sintassi 5.1 evidenzia come l’istruzione di selezione singola if si scriva utilizzando la medesima keyword e una coppia di parentesi tonde ( ) al cui interno si indica, tramite expression, un’espressione di controllo da valutare.

Si scrive, poi, attraverso statement, l’istruzione che dovrà essere eseguita solo se expression sarà diversa da 0 e dunque vera.

Se, viceversa, expression sarà uguale a 0 e dunque falsa, allora il flusso esecutivo del codice si sposterà alla prima istruzione posta subito dopo l’istruzione if medesima.

Sintassi 5.2 Istruzione if che esegue un blocco di istruzioni.

if (expression) 
{
statement_1;
statement_2;
...
statement_N;
}

5_1.jpg

Figura 5.1 Diagramma dell’istruzione di selezione singola if.

La Sintassi 5.2 mostra, invece, come scrivere un’istruzione if che esegue un blocco di istruzioni se expression è diversa da 0. In pratica è sufficiente scrivere le istruzioni di interesse all’interno della consueta coppia di parentesi graffe { }, le quali non necessiteranno del punto e virgola finale che marca, solitamente, un’istruzione.

Listato 5.1 If.c (If).

/* If.c :: Uso dell'istruzione if :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = -1;

// a è minore di 10?
if (a < 10)
printf("a < 10\n");

return (EXIT_SUCCESS);
}

Output 5.1 Dal Listato 5.1 If.c.

a < 10

Nel Listato 5.1 l’istruzione if valuta l’espressione a < 10 la quale ritorna come valore 1, ossia una valore diverso da 0 che è dunque vero, e pertanto viene eseguita la correlativa istruzione printf che manda in output la stringa "a < 10\n".

Snippet 5.1 Espressione di controllo dell’istruzione if come full expression.

int a = 10;
int b = 0;
if (a++ <= 10) // l'espressione è vera: a è minore o uguale a 10
b = a; // qui b varrà 11...

Lo Snippet 5.1 mostra con chiarezza che l’espressione di controllo di un if è considerata una full expression. Infatti, dato che vi è un sequence point tra la valutazione di una full expression e la successiva da valutare, il compilatore valuterà se a è minore o uguale a 10, poi porterà a termine l’effetto collaterale su a, che è quello di incrementarlo di 1 (a++), e infine, dato che l’espressione di controllo sarà risultata vera, assegnerà a b il valore di a che sarà diventato 11.

In pratica, anche se sulla variabile a agisce l’operatore di incremento ++ postfisso, tale incremento non avverrà dopo l’esecuzione dell’assegnamento ma prima, ossia al termine della valutazione dell’espressione di controllo dell’if.

Istruzione di selezione doppia if/else

La struttura di controllo definita di selezione doppia if/else (Sintassi 5.3) permette di eseguire una o più istruzioni se, e solo se, una determinata espressione è vera; altrimenti, se, e solo se, tale espressione è falsa, esegue un’altra o altre istruzioni. I rami di esecuzione delle istruzioni sono mutualmente esclusivi.

Dal punto di vista operativo, l’istruzione if permette di eseguire una sola azione, mentre l’istruzione if/else consente di scegliere tra l’esecuzione di due azioni.

Sintassi 5.3 Istruzione if/else.

if (expression) 
statement;
else
statement;

La Sintassi 5.3 ha la prima parte del tutto simile alla Sintassi 5.1 ma in più ha l’aggiunta della keyword else (che rappresenta una clausola) e di un ulteriore statement che rappresenta l’istruzione che verrà eseguita se expression risulterà pari a 0.

Se però expression risulterà diversa da 0, allora l’istruzione del ramo else non sarà eseguita perché sarà eseguita l’istruzione del ramo if.

Anche in questo caso è possibile definire un blocco di istruzioni da eseguire, nel ramo if oppure nel ramo else, ponendole tra le parentesi graffe { }.

5_2.jpg

Figura 5.2 Diagramma dell’istruzione di selezione doppia if/else.

Listato 5.2 IfElse.c (IfElse).

/* IfElse.c :: Uso dell'istruzione if/else :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 5;

if (a >= 10)
printf("a >= 10\n"); // eseguita se a è maggiore o uguale a 10
else
printf("a < 10\n"); // eseguita in caso contrario

return (EXIT_SUCCESS);
}

Output 5.2 Dal Listato 5.2 IfElse.c.

a < 10

La struttura di controllo if/else può essere costruita con più livelli di annidamento considerando che lo standard stabilisce che un’implementazione ne deve garantire al minimo 127.

Listato 5.3 IfElseNested.c (IfElseNested).

/* IfElseNested.c :: Dimostra l'uso di if/else nidificati :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 3;

if(a >= 10)
printf("a >= 10\n");
else
if(a >= 5)
printf("a >= 5 e a < 10\n");
else
if(a >= 0)
printf("a >= 0 e a < 5\n");

return (EXIT_SUCCESS);
}

Lo stesso codice, è scritto, quasi sempre, nel modo seguente (sicuramente più leggibile), dove ogni else if è scritto nell’ambito della stessa riga e sono indentati esattamente a partire dal primo if.

Listato 5.4 IfElseNestedAndWithIndentation.c (IfElseNestedAndWithIndentation).

/* IfElseNestedAndWithIndentation.c :: if/else nidificati e indentati :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 3;

if (a >= 10)
printf("a >= 10\n");
else if (a >= 5)
printf("a >= 5 e a < 10\n");
else if (a >= 0)
printf("a >= 0 e a < 5\n");

return (EXIT_SUCCESS);
}

È importante rilevare che questa forma di scrittura delle istruzioni if/else non introduce alcuna nuova forma di statement di controllo, ma è solo una maniera per rendere più chiaro l’obiettivo computazionale della struttura di controllo, che è quello di compiere una serie di valutazioni di espressioni laddove solo una o nessuna potrà essere vera e dunque eseguire o meno le istruzioni correlate.

In definitiva una struttura di controllo con una serie di if/else nidificati altro non è che una struttura di selezione doppia if/else dove ogni else ha come istruzione un’altra istruzione if e così via per altre clausole else.

Output 5.3 Dal Listato 5.3 IfElseNested.c e dal Listato 5.4 IfElseNestedAndWithIndentation.c.

a >= 0 e a < 5

L’Output 5.3 rileva che, comunque si scriva la struttura di controllo, la logica è sempre la stessa: se è vera una delle espressioni, allora vengono eseguite le istruzioni corrispondenti e il programma salta al di fuori di tutti gli altri if/else; se nessuna condizione è vera, allora il programma le salta tutte.

Nel nostro caso, dunque, dato che la variabile a vale 3:

  • l’espressione del primo if sarà valutata falsa: a non è maggiore o uguale a 10;
  • l’espressione del secondo if della prima clausola else sarà valutata falsa: a non è maggiore o uguale a 5;
  • l’espressione del terzo if della seconda clausola else sarà valutata vera: a è maggiore o uguale a 0.

Quando si scrivono più strutture if/else, si può incorrere nell’errore denominato dell’else

pendente (dangling else), in cui l’else non è attribuito all corretto e correlativo if.

Listato 5.5 DanglingElse.c (DanglingElse).

/* DanglingElse.c :: Problema dell'else pendente :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 9, b = 3;

if (a > 10)
if (b > 10)
printf("a e b > 10\n"); // eseguita se a e b sono maggiori di 10
else
printf("a < 10\n");

return (EXIT_SUCCESS);
}

L’intento del programma del Listato 5.5 sarebbe quello di far stampare a e b > 10 se le variabili a e b fossero maggiori di 10 e a < 10 nel caso in cui a fosse minore di 10.

Tuttavia, per come è stato scritto il codice, eseguendo il programma non viene stampata l’istruzione dell’else, anche se la variabile a è minore di 10 (è infatti uguale a 9) e quindi soddisfa la condizione corrispondente.

Ciò si verifica perché, come regola, il compilatore associa la clausola else al primo if precedente che trova (a quello cioè lessicalmente più vicino permesso dalla sintassi).

Nel nostro caso, infatti, il compilatore associa la clausola else all’if più vicino che risulta essere quello con l’espressione b > 10.

Per questa ragione il compilatore interpreta le istruzioni nel seguente modo: la variabile a è maggiore di 10? Se lo è, allora valuta se la variabile b è maggiore di 10, e in caso affermativo lo stampa a e b > 10, altrimenti stampa a < 10.

NOTA

Se lanciamo il compilatore GCC con il flag -Wall oppure -Wparentheses otterremo il messaggio warning: suggest explicit braces to avoid ambiguous 'else' [-Wparentheses].

Al fine di ottenere il risultato corretto dovremo scrivere il codice come illustrato di seguito, dove, grazie all’ausilio delle parentesi graffe { } delimitiamo l’if più esterno e rendiamo evidente che è a esso che fa riferimento il correlativo else.

Listato 5.6 CorrectionOfTheDanglingElse.c (CorrectionOfTheDanglingElse).

/* CorrectionOfTheDanglingElse.c :: Problema dell'else pendente corretto :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 9, b = 3;

if (a > 10)
{
if (b > 10)
printf("a e b > 10\n"); // eseguita se a e b sono maggiori di 10
}
else
printf("a < 10\n");

return (EXIT_SUCCESS);
}

Output 5.4 Dal Listato 5.6 CorrectionOfTheDanglingElse.c.

a < 10

Istruzione di selezione multipla switch

La struttura di selezione multipla switch (Sintassi 5.4) consente di eseguire le istruzioni di un blocco di codice, marcato da una particolare etichetta espressa da una clausola case o default, se il valore intero costante che questa rappresenta è uguale (e mai maggiore o minore) al valore dell’espressione di controllo da valutare, che deve essere di tipo intero (per esempio, un tipo char, int, long e così via è legale mentre un tipo float, double e altri no).

Sintassi 5.4 Istruzione switch.

switch (integer_expression)
{
case integer_constant_expression_1:
statements_1;
[break];
case integer_constant_expression_2:
statements_2;
[break];
case integer_constant_expression...:
statements...;
[break];
case integer_constant_expression_N:
statements_N;
[break];
[default:
statements_N;
[break;]
]
}

Per costruire un’istruzione di selezione multipla si deve procedere come segue.

  1. Si scrive la keyword switch seguita da una coppia di parentesi tonde ( ) al cui interno si indica un’espressione intera da valutare. Si definisce, quindi, tra le consuete parentesi graffe { } il body dello switch stesso.
  2. Si scrivono delle etichette case che indicano delle espressioni costanti intere i cui valori saranno confrontati con il valore ritornato dalla valutazione dell’espressione intera dello switch. Ogni etichetta case potrà avere una o più istruzioni che saranno eseguite se, e solo se, il valore della sua espressione sarà uguale al valore dell’espressione dell’istruzione switch. Eventualmente, come statement finale, si può scrivere un’istruzione break che trasferisce l’esecuzione del codice all’istruzione successiva all’istruzione switch (di fatto break provoca un’uscita immediata dal blocco switch).
  3. Si scrive, nel caso, un’etichetta espressa dalla clausola default che indica una o più istruzioni che saranno eseguite se nessun caso soddisferà l’espressione indicata dallo switch.

Prima di vedere degli esempi pratici di utilizzo del costrutto switch è importante dare anche altre indicazioni: se le etichette case hanno due o più istruzioni, le stesse possono essere scritte senza racchiuderle tra le parentesi graffe { }; non possono esservi etichette case duplicate nell’ambito dello stesso switch (in pratica più espressioni dei casi non possono rintonare lo stesso valore) e l’ordine di scrittura delle stesse non ha importanza; ci può essere solo un’etichetta default che in genere è posta come ultima label (è possibile collocarla ovunque all’interno dello switch).

5_3.jpg

Figura 5.3 Diagramma dell’istruzione di selezione multipla switch (secondo un comune ordine di scrittura).

Listato 5.7 SwitchCase.c (SwitchCase).

/* SwitchCase.c :: Uso dell'istruzione switch :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int number = 4;

// valuto number
switch (number)
{
case 1: // vale 1?
printf("number = 1\n");
break;
case 2: // vale 2?
printf("number = 2\n");
break;
case 3: // vale 3?
printf("number = 3\n");
break;
case 4: // vale 4?
printf("number = 4\n");
break;
default: // nessuna corrispondenza?
printf("number = [no matching]\n");
}

return (EXIT_SUCCESS);
}

Output 5.5 Dal Listato 5.7 SwitchCase.c.

number = 4

Nel Listato 5.7 la keyword switch valuta il valore della variabile number (in questo caso 4) e cerca una corrispondenza tra i valori delle etichette case. Se trova un’etichetta case che soddisfa tale valutazione (e nel nostro caso la trova: case 4:), allora ne esegue le istruzioni ivi indicate: l’una si limita a stampare i caratteri number = 4 tramite printf; l’altra, break, esce dallo switch e fa riprendere l’esecuzione del programma dalla prossima istruzione che è identificata dalla keyword return.

Più etichette case possono essere “raggruppate” in modo da esplicitare delle istruzioni che saranno eseguite se uno qualsiasi dei valori delle relative espressioni corrisponderà al valore dell’espressione dello switch.

Listato 5.8 GroupingOfCaseLabels.c (GroupingOfCaseLabels).

/* GroupingOfCaseLabels.c :: Etichette case raggruppate :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
char letter = 'e';

// FALL THROUGH esplicito: è buona norma inserire un commento come questo...
switch (letter)
{
// lettere a, b, c ?
case 'a':
case 'b':
case 'c':
printf("Tra le lettere a, b, c\n");
break;
// lettere d, e, f ?
case 'd':
case 'e':
case 'f':
printf("Tra le lettere d, e, f\n");
break;
// nessuna corrispondenza
default:
printf("Nessuna corrispondenza di lettera");
break; // non necessario ma utile in caso di ulteriori case posti dopo...
}

return (EXIT_SUCCESS);
}

Output 5.6 Dal Listato 5.8 GroupingOfCaseLabels.c.

Tra le lettere d, e, f

Il Listato 5.8 evidenzia che per raggruppare più etichette case è sufficiente indicarle le une dopo le altre e poi, dopo l’ultima, scrivere le istruzioni che saranno eseguite con questo scopo.

Nel nostro caso, switch valuta la variabile letter, che ritorna il valore intero ASCII del carattere e (101 in base decimale), e poi verifica se esiste un’etichetta case con quel valore. La verifica ha esito favorevole (case 'e':) e, quindi, a partire dal quel punto, inizia a scorrere il codice finché non trova una qualche istruzione eseguibile. Indipendentemente se vi siano o meno altre etichette case, il codice eseguito sarà quello indicato dall’etichetta case 'f':, ma ciò non causerà alcun errore logico nel programma perché le etichette case sono state scritte proprio con l’obiettivo di stampare i caratteri Tra le lettere d, e, f se il valore dell’espressione letter ricade in quel range di caratteri.

Infine, notiamo come l’etichetta default abbia come ultima istruzione break. Questa, ancorché non necessaria (se non vi fosse, dopo l’esecuzione della relativa istruzione printf, il flusso esecutivo del codice si sposterebbe alla parentesi graffa di chiusura dello switch e quindi all’istruzione return posta dopo di esso) può diventare importante se, successivamente, decidiamo di inserire un’etichetta case dopo l’etichetta default.

Infatti, in assenza del break, se non vi fossero espressioni delle etichette case in grado di soddisfare l’espressione di controllo dello switch, il controllo passerebbe all’etichetta default e poi anche all’altra etichetta case eseguendone in modo non opportuno il relativo codice.

TERMINOLOGIA

Il comportamento eseguito dall’istruzione switch per cui quando l’esecuzione di un caso è terminata passa in automatico al caso successivo (in cascata), e se non esplicitamente negato tramite un’istruzione break, è definita falls through.

Snippet 5.2 Espressione di controllo dell’istruzione switch come full expression.

int a = 9;
int b = 100;

switch (a++) // effetto collaterale: a cambia valore da 9 a 10
{
case 9: b = b * a + 250; // questo è il ramo eseguito... ma a varrà 10 e non 9
break;
case 10: b = b * a + 500;
break;
default: break;
}

Lo Snippet 5.2 mostra un ulteriore esempio del fatto che un’espressione di controllo è una full expression: infatti, la valutazione dell’espressione a dello switch darà come risultato 9 che corrisponderà all’etichetta case 9: laddove, quando il compilatore eseguirà la relativa istruzione, a sarà stata prima incrementata a 10 dall’operatore postfisso ++.

Istruzioni di iterazione

Le istruzioni di iterazione (iteration statement) consentono di eseguire una o più istruzioni (loop body) in modo ripetuto (ciclico), finché un’espressione di controllo non diventa falsa ossia uguale a 0. L’espressione di controllo delle istruzioni while e do e le espressioni facoltative dell’istruzione for sono considerate delle full expression, e dunque qualsiasi eventuale effetto collaterale che possa lì prodursi sarà valutato prima della valutazione di una successiva full expression.

TERMINOLOGIA

A volte le istruzioni di iterazione sono denominate loop condizionali (conditional loop) perché l’esecuzione ciclica delle istruzioni relative dipenderà da una “condizione” evidenziata dall’espressione di controllo. Se, per esempio, scrivessimo un’espressione di controllo per un loop come a < 10, è come se ponessimo come condizione per l’esecuzione ciclica delle sue istruzioni quella che a deve essere minore del valore 10.

Istruzione di iterazione while

La struttura di iterazione while (Sintassi 5.5) permette di eseguire lo stesso blocco di istruzioni ripetutamente finché una determinata espressione è vera ossia diversa da 0.

Sintassi 5.5 Istruzione while.

while (expression)
statement;

In pratica per utilizzare tale istruzione di iterazione utilizziamo la keyword while, una coppia di parentesi tonde ( ) al cui interno vi sarà l’espressione di controllo da valutare e l’istruzione da eseguire finché expression sarà diversa da 0. È altresì possibile definire due o più istruzioni da eseguire ciclicamente ponendole tra le parentesi graffe { }.

5_4.jpg

Figura 5.4 Diagramma dell’istruzione di iterazione while.

Listato 5.9 While.c (While).

/* While.c :: Uso dell'istruzione while :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 8;

printf("a = [ ");
while (a >= 0) // finché a >= 0 esegue il ciclo
printf("%d ", a--);

printf("]\n");

return (EXIT_SUCCESS);
}

Output 5.7 Dal Listato 5.9 While.c.

a = [ 8 7 6 5 4 3 2 1 0 ]

Nel programma del Listato 5.9 il ciclo while si può interpretare in questo modo: finché la variabile a è maggiore o uguale al valore 0, stampane il valore ripetutamente. Nel blocco di codice del while l’istruzione a-- è fondamentale poiché permette di decrementarne il valore. Se non ci fosse questa istruzione, il ciclo while sarebbe infinito, perché la condizione sarebbe sempre vera (diversa da 0), dato che la variabile a sarebbe sempre maggiore o uguale a 0.

Ripetiamo, per chiarire meglio come si comporta il flusso esecutivo della struttura iterativa while:

  1. Controlla se a è >= 0 e se lo è va al punto 2, altrimenti va al punto 3.
  2. Esegue l’istruzione di stampa, decrementa a e ritorna al punto 1.
  3. Esce dal ciclo.

Si nota che, se l’espressione è subito falsa (uguale a 0), le istruzioni nel corpo della struttura while non verranno mai eseguite.

In pratica, in un’istruzione di iterazione while, l’espressione di controllo è sempre valutata “prima” che le istruzioni che ne compongono il loop body siano eseguite anche una sola volta (potrebbero, quindi, non essere mai eseguite se l’espressione di controllo è subito falsa).

Snippet 5.3 Espressione di controllo dell’istruzione while come full expression.

int j = 1;
int a = -1;

while (j--)
a = j; // qua a varrà 0

Lo Snippet 5.3 evidenzia che quando il compilatore elaborerà l’istruzione while, prima verificherà se j è diversa da 0 e poi completerà il side effect dell’operatore postfisso ++ che la decrementerà a 0. Poi assegnerà ad a quel valore.

In pratica anche se l’operatore ++ è postfisso, non assegnerà prima ad a il valore originario di j, ossia 1, e poi decrementerà j.

Istruzione di iterazione do/while

La struttura di iterazione do/while (Sintassi 5.6) consente, analogamente alla struttura while, di ripetere un blocco di istruzioni ripetutamente finché un’espressione è vera. In questo caso, a differenza di while, le istruzioni del corpo della struttura do vengono eseguite almeno una volta, poiché la valutazione dell’espressione di controllo viene effettuata dopo che il flusso esecutivo del codice ha raggiunto l’istruzione while posta in coda.

Sintassi 5.6 Istruzione do.

do
statement;
while (expression);

Tale istruzione si utilizza scrivendo la keyword do, l’istruzione da eseguire finché l’espressione di controllo sarà diversa da 0, la keyword while, una coppia di parentesi tonde ( ) al cui interno vi sarà l’espressione di controllo da valutare e il carattere punto e virgola finale.

È inoltre possibile definire due o più istruzioni da eseguire ciclicamente ponendole tra le parentesi graffe { }.

5_5.jpg

Figura 5.5 Diagramma dell’istruzione di iterazione do/while.

Listato 5.10 DoWhile.c (DoWhile).

/* DoWhile.c :: Uso dell'istruzione do/while :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 8;

printf("a = [ ");
do // parentesi non necessarie... scritte solo per maggiore chiarezza
{
printf("%d ", a--);
}
while (a >= 0); // finché a >= 0 esegue il ciclo

printf("]\n");

return (EXIT_SUCCESS);
}

Output 5.8 Dal Listato 5.10 DoWhile.c.

a = [ 8 7 6 5 4 3 2 1 0 ]

Il ciclo del Listato 5.10 si comporta allo stesso modo di quello del Listato 5.9 ma, a differenza di esso, stampa il valore di a e poi lo decrementa almeno una volta, anche se potrebbe essere subito minore di 0, e poi verifica se a è maggiore o uguale a 0.

Il flusso esecutivo del blocco do/while è il seguente.

  1. Esegue l’istruzione di stampa e decrementa a.
  2. Controlla se a è >= 0 e se lo è va al punto 1, altrimenti va al punto 3.
  3. Esce dal ciclo.

Si nota che, se l’espressione è subito falsa (uguale a 0), le istruzioni nel corpo della struttura do/while verranno eseguite almeno una volta.

In pratica, in un’istruzione di iterazione do/while, l’espressione di controllo è sempre valutata “dopo” che le istruzioni che ne compongono il loop body sono state eseguite anche una sola volta (potrebbero, quindi, essere state eseguite anche se l’espressione di controllo è subito falsa).

Snippet 5.4 Espressione di controllo dell’istruzione do/while come full expression.

int a = 3;
int b = 2;
int j = 1;

do
{
// prima della verifica della condizione a varrà 1
// dopo le valutazioni delle espressioni nel while a varrà 6...
a = j;
if (a == 6)
break;
}
while (j = (a + 1, b += a, b));

Lo Snippet 5.4 mostra come anche nell’ambito di un ciclo do/while tutti i side effect saranno valutati prima di eseguire il loop body.

Infatti sia la variabile j sia la variabile b subiranno una modifica del loro valore in memoria, e tali modifiche saranno attuate prima di entrare nel corpo del ciclo e ciò perché, ribadiamo, vi sono due sequence point: il primo è marcato dall’operatore virgola (,) e il secondo è marcato dall’espressione di controllo del while che è una full expression.

Istruzione di iterazione for

La struttura di iterazione for (Sintassi 5.7) consente, come le strutture while e do/while, di ripetere un blocco di istruzioni finché un’espressione è vera (diversa da 0); a differenza di while e do/while, for consente di gestire nell’ambito del suo costrutto delle espressioni aggiuntive con cui, generalmente, si inizializzano e modificano delle variabili di controllo.

Sintassi 5.7 Istruzione for.

for ([decl_OR_expr_1]; [expression_2]; [expression_3])
statement;

Di fatto, per usare tale struttura, si scrive la keyword for seguita dalle parentesi tonde ( ) che racchiudono tre espressioni opzionali di cui la prima può agire anche come istruzione di dichiarazione. Abbiamo:

  • decl_OR_expr_1, che esprime una dichiarazione di una o più variabili che possono essere impiegate dalle altre espressioni del for o nell’ambito del suo loop body oppure un’espressione che viene valutata inizialmente prima dell’espressione di controllo. Segue un punto e virgola;
  • expression_2, che indica l’espressione di controllo che viene valutata prima dell’esecuzione del loop body del for e che controlla, quindi, la condizione di terminazione del ciclo medesimo. Segue un punto e virgola;
  • expression_3, che designa l’espressione da valutare dopo l’esecuzione del loop body del for.

Termina la definizione della struttura for l’istruzione (statement) da eseguire ciclicamente finché l’espressione di controllo sarà vera.

Se si vogliono eseguire in loop due o più istruzioni, anche il ciclo for può racchiuderle tra la coppia di parentesi graffe { } proprie di una compound statement.

NOTA

È solo a partire dallo standard C99 che è possibile utilizzare un’istruzione di dichiarazione di una o più variabili direttamente come prima espressione. Prima di esso, infatti, la prima espressione doveva essere una mera espressione (Snippet 5.5 e 5.6).

Snippet 5.5 Prima espressione e modalità di utilizzo C11.

// dichiarazione della variabile i direttamente come prima espressione:
// C11 consentito
for (int i = 0; i < 10; i++)
; // fai qualcosa...

Snippet 5.6 Prima espressione e modalità di utilizzo C90.

/*
la dichiarazione della variabile i è posta fuori dal costrutto for;
nel costrutto for la prima espressione è una mera espressione
*/
int i;
for (i = 0; i < 10; i++)
; /* fai qualcosa... */

Analizzando la definizione di un’istruzione for possiamo trovare una certa equivalenza con l’istruzione while: in pratica qualsiasi loop creato con un costrutto for è creabile con un costrutto while, come mostra la generalizzazione di entrambi di cui la Sintassi 5.8.

Sintassi 5.8 Equivalenza tra il costrutto for e il costrutto while.

// un ciclo for...
for (expression_1; expression_2; expression_3)
statement;

// un ciclo while...
expression_1;
while (expression_2)
{
statement;
expression_3;
}

5_6.jpg

Figura 5.6 Diagramma dell’istruzione di iterazione for.

Listato 5.11 For.c (For).

/* For.c :: Uso dell'istruzione for :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
printf("a = [ ");
for (int a = 8; a >= 0; a--) // finché a >= 0 esegue il ciclo
printf("%d ", a);

printf("]\n");

return (EXIT_SUCCESS);
}

Output 5.9 Dal Listato 5.11 For.c.

a = [ 8 7 6 5 4 3 2 1 0 ]

Esaminando il Listato 5.11 vediamo che la struttura iterativa for impiegata è formata dai seguenti blocchi costitutivi: un’istruzione di dichiarazione, eseguita solo una volta, in cui è inizializzata una variabile di controllo (visibile solamente nel ciclo for), rappresentata dall’istruzione int a = 8; un’espressione di controllo della condizione di continuazione del ciclo, rappresentata dall’istruzione a >= 0; un’espressione di modifica della variabile di controllo, rappresentata dall’istruzione a--.

Il flusso esecutivo del ciclo è invece il seguente.

  1. Dichiara e inizializza la variabile a.
  2. Controlla se a >= 0 e se lo è va al punto 3, altrimenti va al punto 6.
  3. Stampa il valore di a.
  4. Decrementa la variabile a.
  5. Ritorna al punto 2.
  6. Esce dal ciclo.

TERMINOLOGIA

Per ambito di visibilità si intende una regione del codice sorgente dove un identificatore è accessibile e dunque utilizzabile. Ritorneremo su questo importante concetto nel Capitolo 9, “Dichiarazioni”.

Vediamo un altro esempio di utilizzo (Listato 5.12) di un ciclo for dove la prima espressione dichiara e inizializza più variabili, mentre la terza espressione ne modifica il valore.

Listato 5.12 ComplexExpressionsWithFor.c (ComplexExpressionsWithFor).

/* ComplexExpressionsWithFor.c :: Uso dell'istruzione for con espressioni complesse :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int var1 = 3, var2 = 2;

printf("a\tz\n");
for (int a = var1 * 2 + var2, z = 0; a >= 0; a--, z++)
printf("%d\t%d\n", a, z);

return (EXIT_SUCCESS);
}

Output 5.10 Dal Listato 5.12 ComplexExpressionsWithFor.c.

a       z
8 0
7 1
6 2
5 3
4 4
3 5
2 6
1 7
0 8

In definitiva la prima espressione di un ciclo for può contenere una dichiarazione di più variabili utilizzando il carattere virgola (,) che agisce come separatore, oppure più espressioni usando sempre il carattere virgola ma che agisce come operatore.

La terza espressione può altresì contenere più espressioni usando ancora l’operatore virgola.

I blocchi costitutivi della struttura iterativa for (decl_OR_expr_1, expression_2 e expression_3) si possono anche omettere, a condizione però che i punti e virgola ; di separazione vengano scritti. Quando, tuttavia, viene omesso expression_2, il compilatore lo sostituisce con una costante diversa da 0 e pertanto ne rende infinito il ciclo.

Listato 5.13 ForWithoutExpressions.c (ForWithoutExpressions).

/* ForWithoutExpressions.c :: Uso dell'istruzione for senza le espressioni :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 8;

printf("a = [ ");
for (;;) // ciclo infinito che è interrotto dal break
{
if (a < 0)
break; // senza questa istruzione il ciclo diventa infinito
printf("%d ", a--);
}
printf("]\n");

return (EXIT_SUCCESS);
}

Output 5.11 Dal Listato 5.13 ForWithoutExpressions.c.

a = [ 8 7 6 5 4 3 2 1 0 ]

Nel Listato 5.13 notiamo come le espressioni costituenti la struttura iterativa siano state poste prima dell’istruzione for per la definizione della variabile di controllo, e all’interno del blocco di istruzioni del ciclo per il controllo di terminazione e per la modifica della variabile di controllo. Vediamo, infine, che i cicli for si possono costruire anche come cicli senza corpo. Essi devono però contenere sempre almeno un’istruzione definita come vuota o nulla (carattere punto e virgola).

Listato 5.14 ForWithoutBody.c (ForWithoutBody).

/* ForWithoutBody.c :: Uso dell'istruzione for senza un loop body :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int val_max = 100, i = 0;

for (i = 0; i < val_max; i++) // ciclo senza corpo
; // istruzione nulla
printf("i = %d\n", i); // i vale 100!!!

return (EXIT_SUCCESS);
}

Output 5.12 Dal Listato 5.14 ForWithoutBody.c.

i = 100

ATTENZIONE

Quando si utilizza un’istruzione nulla bisogna fare attenzione a non incorrere in un errore di tipo logico come quello mostrato dallo Snippet 5.7, dove, quando a == -5, il compilatore esegue l’istruzione nulla e anche quella di stampa del valore 5, che probabilmente non si aveva intenzione di eseguire. Lo Snippet 5.8 mostra invece un errore in tipo sintattico: in questo caso il compilatore non troverà un if da associare all’ultimo else perché il primo if non ha racchiuso le sue istruzioni in un blocco delimitato dalle parentesi graffe { }.

Snippet 5.7 Errore logico con un’istruzione nulla.

int a = -5;
if (a == -5); // ERRORE LOGICO
printf("5\n");

Snippet 5.8 Errore sintattico con un’istruzione nulla.

int a = -5;
if (a == -5);
printf("-5\n");
else // error: 'else' without a previous 'if'
printf("non -5\n");

for e while

Come detto, se esiste un’equivalenza tra un costrutto for e un costrutto while, quando usare l’uno e quando usare l’altro? La risposta, quantunque possa essere legata a un gusto personale, può anche essere data facendo le seguenti considerazioni di ordine pratico: usare un’istruzione for quando si ha la necessità di creare dei loop che effettuano delle operazioni di inizializzazione e aggiornamento di variabili che fungono da contatori; usare un’istruzione while quando si ha l’esigenza di definire dei loop che devono eseguire le relative operazioni solo al verificarsi una determinata condizione.

Snippet 5.9 Espressioni del ciclo for come full expression.

int a = 3;
int b = 2;
int j = 1;

for (a++; b < a++ +10; j = ++b)
{
printf("%d - % d - %d\n", a, b, j);
if (a == 7) // interrompi il ciclo altrimenti sarebbe infinito...
break;
}

Analizziamo come il compilatore processa ogni espressione del ciclo for in accordo con le regole di priorità delle valutazioni delle espressioni e del loop body e avendo la consapevolezza che ciascuna marca un sequence point (sono infatti delle full expression):

  1. a++: al termine della valutazione a avrà il valore 4;
  2. b < a++ +10: al termine della valutazione b avrà ancora il valore 2 e a avrà il valore 5;
  3. printf("%d - % d - %d\n", a, b, j): saranno stampati i valori: per a 5, per b 2 e per j 1;
  4. j = ++b: al termine della valutazione b conterrà il valore 3 così come j;
  5. b < a++ +10: al termine della valutazione b avrà ancora il valore 3 e a avrà il valore 6;
  6. printf("%d - % d - %d\n", a, b, j): saranno stampati i valori: per a 6, per b 3 e per j 3;
  7. j = ++b: al termine della valutazione b conterrà il valore 4 così come j;
  8. b < a++ +10: al termine della valutazione b avrà ancora il valore 4 e a avrà il valore 7;
  9. printf("%d - % d - %d\n", a, b, j): saranno stampati i valori: per a 7, per b 4 e per j 4. In più il ciclo si interromperà a causa dell’istruzione break perché a == 7 sarà vera.

Istruzioni di salto

Le istruzioni di salto consentono di trasferire, spostare il controllo dell’esecuzione del codice in un altro punto e in modo incondizionato, ossia senza la dipendenza da alcuna espressione di controllo.

Istruzione break

Un’istruzione break consente di interrompere l’esecuzione del codice posto all’interno di un ciclo espresso mediante le keyword while, do/while e for e contestualmente di trasferirne il controllo alla successiva istruzione.

Questa istruzione può comparire oltre che all’interno di un loop body anche all’interno di un costrutto switch, in relazione a una determinata clausola case, dove adempie allo stesso scopo.

Listato 5.15 Break.c (Break).

/* Break.c :: Uso dell'istruzione break :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
printf("a = ");
for (int a = 1; a <= 10; a++) // finché a <= 10
{
if (a == 5)
{
break;
}
printf("%d ", a);
}
printf("\n");

int a = 1;

printf("a = ");
while (a <= 10) // finché a <= 10
{
if (a == 5)
break;
printf("%d ", a++);
}
printf("\n");

return (EXIT_SUCCESS);
}

Output 5.13 Dal Listato 5.15 Break.c.

a = 1 2 3 4
a = 1 2 3 4

L’istruzione break nel Listato 5.15 interrompe l’iterazione sia del ciclo for sia del ciclo while; infatti quando la variabile a è uguale a 5 il programma esce dall’iterazione, pertanto saranno stampati solo i valori fino a 4.

Di fatto, un’istruzione break è utile per interrompere un ciclo “nel mezzo” della sua esecuzione rispetto a una comune interruzione che potrebbe avvenire all’inizio (nel caso di un while o di un for dove l’exit point è definito dall’espressione di controllo posta prima o all’inizio del loop body) oppure alla fine (nel caso di un do/while dove l’exit point è definito dall’espressione di controllo posta dopo o alla fine del loop body).

Quando si utilizza un’istruzione break è importante tenere presente che essa interrompe l’esecuzione del codice della “più piccola” istruzione while, for, do/while o switch innestata (Snippet 5.10).

Snippet 5.10 Istruzione break e cicli innestati.

int a = 0, b = 0, c = 0;

// al termine dell'iterazione dei cicli le variabili a, b e c
// conterranno rispettivamente i valori 10, 10 e 5 e ciò dimostrerà
// che il I e il II ciclo non sono stati interrotti dal break del III ciclo
while (a < 10) // I ciclo
{
a++;
while (b < 10) // II ciclo innestato nel I
{
b++;
while (c < 10) // III ciclo innestato nel II
{
if (c == 5)
break; // interrompe solo il III ciclo!
c++;
}
}
}

Istruzione continue

Un’istruzione continue consente di saltare alla fine di un loop body, evitando il processing di eventuali istruzioni poste dopo di esse, e di riprendere il prossimo step di esecuzione. Non esce mai, dunque, dal relativo loop body.

Questa istruzione può comparire solo all’interno di un loop body espresso dalle keyword while, do/while e for ma mai all’interno di un costrutto switch che non sia innestato in un costrutto di iterazione dove, in quest’ultimo caso, causa un salto alla fine del loop body dal costrutto.

Nel caso di un ciclo while e do/while il prossimo step di esecuzione è la valutazione della relativa espressione di controllo, mentre nel caso di un ciclo for il prossimo step di esecuzione è la valutazione della terza espressione (in genere quella che aggiorna il valore di una variabile di controllo) e poi della seconda (in genere quella che verifica una condizione di terminazione del ciclo).

Listato 5.16 Continue.c (Continue).

/* Continue.c :: Uso dell'istruzione continue :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
printf("a = ");
for (int a = 1; a <= 10; a++) // finché a <= 10
{
if (a == 5) // salta l'istruzione successiva se a == 5
continue;
printf("%d%c ", a, a != 10 ? ',' : ' ');
// continue fa spostare il flusso di esecuzione qui;
// poi il ciclo riprende con a++ quindi a <= 10
}
printf("\n");

int a = 1;

printf("a = ");
while (a <= 10) // finché a <= 10
{
if (a == 5) // salta le istruzioni successive se a == 5
{
a++;
continue;
}
printf("%d%c ", a, a != 10 ? ',' : ' ');
a++;
// continue fa spostare il flusso di esecuzione qui;
// poi il ciclo riprende con a <= 10
}
printf("\n");

return (EXIT_SUCCESS);
}

Output 5.14 Dal Listato 5.16 Continue.c.

a = 1, 2, 3, 4, 6, 7, 8, 9, 10
a = 1, 2, 3, 4, 6, 7, 8, 9, 10

Il Listato 5.16 mostra come nella pratica l’istruzione continue salti le rimanenti istruzioni del corpo di una struttura iterativa, procedendo quindi con la successiva iterazione.

Nel nostro esempio, nonostante i valori stampati dal for e dal while saranno, ugualmente, da 1 a 10 eccetto il 5, i due cicli avranno un differente flusso esecutivo in ragione anche di quanto prima detto sul “dove” il codice riprende l’esecuzione dopo l’esecuzione di un’istruzione continue.

Infatti, per la sequenza del for avremo quanto segue.

  1. Inizializza la variabile a = 1.
  2. Controlla se a <= 10 e se lo è va al punto 3, altrimenti va al punto 6.
  3. Controlla se a == 5 e se lo è non esegue il punto 4 ma va al punto 5. Se, viceversa, a == 5 è falsa, allora va al punto 4.
  4. Esegue l’istruzione printf di stampa del valore di a.
  5. Incrementa a e va al punto 2.
  6. Esce dal ciclo.

Per la sequenza del while avremo invece quanto segue.

  1. Controlla se a <= 10 e se lo è va al punto 2, altrimenti va al punto 5.
  2. Controlla se a == 5 e se lo è non va al punto 3, incrementa a di 1 altrimenti il ciclo diventa infinito e poi va al punto 1. Se, viceversa, a == 5 è falsa, allora va al punto 3.
  3. Esegue l’istruzione printf di stampa del valore di a.
  4. Incrementa a e va al punto 1.
  5. Esce dal ciclo.

Infine, così come abbiamo visto nel caso di un’istruzione break, anche un’istruzione continue produrrà i suoi effetti solamente nell’ambito della “più piccola” istruzione while, for o do/while innestata.

Così, se per esempio abbiamo due cicli for innestati, un’istruzione continue elaborata all’interno del ciclo for più interno farà spostare il flusso esecutivo del codice alla fine di tale for e non di quello a esso esterno.

Istruzione goto

Un’istruzione goto permette di saltare verso un punto del codice arbitrario marcato da un identificatore scritto con una particolare sintassi che lo individua come un’etichetta (label). L’etichetta e l’istruzione goto devono trovarsi nell’ambito della stessa funzione.

NOTA

A partire dallo standard C99 esiste anche la seguente restrizione all’uso del goto (Snippet 5.11): non può essere usato prima della dichiarazione di un array di lunghezza variabile per “aggirarla” e trasferire il flusso di esecuzione del codice dopo di essa.

Snippet 5.11 Istruzione goto e VLA: restrizione all’uso.

    int val;
int dim = 10;
goto label; // error: jump into scope of identifier with variably modified type
int a[dim];

a[2] = 100;

label: // etichetta
val = 10;

Per quanto attiene quest’istruzione, è bene subito dire che il suo uso è, in linea generale, sconsigliato e ciò per le seguenti ragioni: può produrre il cosiddetto spaghetti code ossia codice disordinato e non manutenibile che salta da un punto all’altro del programma, avanti e indietro e in modo intrecciato; può essere sostituito da forme specializzate e ristrette di goto quali sono le istruzioni break, continue e return e, alcune volte, anche dalla funzione exit dichiarata nel file header <stdlib.h>.

Comunque, in ragione della filosofia pragmatica e libertaria di C, i progettisti del linguaggio hanno ritenuto opportuno lasciare tale istruzione e demandare agli sviluppatori la scelta se usarla o meno in base alle loro esigenze.

In effetti, a ben pensare, ci sarebbero i seguenti casi (Listato 5.17) dove potrebbe essere utile e conveniente utilizzare il goto:

  • per uscire direttamente da una serie di cicli profondamente annidati (ricordiamo, infatti, che un’istruzione break interrompe solo il ciclo corrente e non un altro in cui eventualmente lo stesso sia annidato);
  • per uscire da un ciclo da un’istruzione switch che è in esso annidata.

Listato 5.17 Goto.c (Goto).

/* Goto.c :: Uso dell'istruzione goto :: */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a = 0, b = 0, c = 0;

while (a < 10) // I ciclo
{
a++;
while (b < 10) // II ciclo innestato nel I
{
b++;
while (c < 10) // III ciclo innestato nel II
{
if (c == 5)
goto print; // salta direttamente all'etichetta print
c++;
}
}
}

// istruzione etichettata raggiunta dal goto...
print:{ printf("a = %d, b = %d, c = %d\n", a, b, c); c = 1; }
for (;;) // ciclo infinito
{
switch (c)
{
case 1:
case 2:
case 3:
case 4:
case 5: printf("c contiene un valore ancora inferiore a 6...\n"); break;
case 6: goto end; // salta direttamente all'etichetta end
}
c++;
}

end: // etichetta
printf("c contiene finalmente il valore 6!!!\n");

return (EXIT_SUCCESS);
}

Output 5.15 Dal Listato 5.17 Goto.c.

a = 1, b = 1, c = 5
c contiene un valore ancora inferiore a 6...
c contiene un valore ancora inferiore a 6...
c contiene un valore ancora inferiore a 6...
c contiene un valore ancora inferiore a 6...
c contiene un valore ancora inferiore a 6...
c contiene finalmente il valore 6!!!

Il Listato 5.17 definisce tre cicli while, dove il primo dovrebbe iterare finché a è minore di 10, il secondo finché b è minore di 10 e il terzo finché c è minore di 10.

Tuttavia, nel terzo ciclo, quando c è uguale a 5 decidiamo di “interrompere” bruscamente tutte e tre le iterazioni e trasferire, mediante un’istruzione goto, il flusso di esecuzione del codice all’etichetta print, la quale stampa a video il valore delle variabili a, b e c.

Poi, definiamo un ciclo for dove, finché il valore della variabile c è inferiore a 6 stampiamo una determinata stringa di caratteri che lo rammenta, mentre quando c è uguale a 6, giacché il ciclo for è infinito, decidiamo tramite goto di uscire direttamente da esso e passare il controllo del flusso del codice alla label end, la cui istruzione etichettata manderà a video un’altra stringa informativa.

A parte le eccezioni citate, che in ogni caso è sempre possibile evitare anche se al prezzo di rendere il codice più complesso (Snippet 5.12), ribadiamo che l’utilizzo del goto non dovrebbe mai essere preso in considerazione.

Snippet 5.12 Eliminazione dell’istruzione goto.

int c = 1;
for (;;) // ciclo infinito
{
switch (c)
{
case 1:
case 2:
case 3:
case 4:
case 5: printf("c contiene un valore ancora inferiore a 6...\n");
break;
case 6: break;
}
if (c == 6)
break;
else
c++;
}
printf("c contiene finalmente il valore 6!!!\n");

Istruzione return

Un’istruzione return permette di terminare l’esecuzione della corrente funzione e di ritornare, trasferire il controllo del flusso di esecuzione del codice a un’altra funzione chiamante.

Eventualmente, tale istruzione può anche ritornare al chiamante un valore che, prima di essere restituito, è convertito nel tipo di ritorno esplicitato all’atto di dichiarazione della funzione medesima.

NOTA

Un dettaglio significativo su tale istruzione sarà fornito nel Capitolo 6 e ciò perché una sua piena comprensione potrà essere raggiunta solo dopo aver affrontato le funzioni. È apparso comunque opportuno citarla in questa sede perché un’istruzione return è categorizzata come un’istruzione di salto, e dunque è qui che il lettore ne deve avere una prima indicazione sia termologica sia semantica, per quanto in modo breve e sommario.

Istruzioni etichettate

Le istruzioni etichettate rappresentano delle statement che sono precedute da apposite etichette (label) definibili attraverso le seguenti sintassi.

Sintassi 5.9 Etichetta identificatore.

identifier: statement

La Sintassi 5.9 definisce un’etichetta attraverso l’indicazione di un identificatore seguito dal carattere due punti (:) e il suo unico caso di utilizzo è con l’istruzione goto, per la quale rappresenta un target, ossia una destinazione.

Sintassi 5.10 Etichetta case.

case integer_constant_expression: statement

Sintassi 5.11 Etichetta default.

default: statement

La Sintassi 5.10 e la Sintassi 5.11 definiscono le comuni etichette case e default già viste e trattate nell’ambito dell’istruzione di selezione multipla switch che, ricordiamo, è l’unico costrutto all’interno del quale è possibile utilizzarle.