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).
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;
}
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 {
}
.
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 a10
; - l’espressione del secondo
if
della prima clausolaelse
sarà valutata falsa:a
non è maggiore o uguale a5
; - l’espressione del terzo
if
della seconda clausolaelse
sarà valutata vera:a
è maggiore o uguale a0
.
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.
- 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 delloswitch
stesso. - 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 delloswitch
. Ogni etichettacase
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’istruzioneswitch
. Eventualmente, come statement finale, si può scrivere un’istruzionebreak
che trasferisce l’esecuzione del codice all’istruzione successiva all’istruzioneswitch
(di fattobreak
provoca un’uscita immediata dal bloccoswitch
). - 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 dalloswitch
.
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
).
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 { }
.
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
:
- Controlla se
a
è>= 0
e se lo è va al punto 2, altrimenti va al punto 3. - Esegue l’istruzione di stampa, decrementa
a
e ritorna al punto 1. - 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 {
}
.
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.
- Esegue l’istruzione di stampa e decrementa
a
. - Controlla se
a
è>= 0
e se lo è va al punto 1, altrimenti va al punto 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 delfor
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 delfor
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 delfor
.
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;
}
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.
- Dichiara e inizializza la variabile
a
. - Controlla se
a >= 0
e se lo è va al punto 3, altrimenti va al punto 6. - Stampa il valore di
a
. - Decrementa la variabile
a
. - Ritorna al punto 2.
- 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):
a++
: al termine della valutazionea
avrà il valore4
;b < a++ +10
: al termine della valutazioneb
avrà ancora il valore2
ea
avrà il valore5
;printf("%d - % d - %d\n", a, b, j)
: saranno stampati i valori: pera
5
, perb
2
e perj
1
;j = ++b
: al termine della valutazioneb
conterrà il valore3
così comej
;b < a++ +10
: al termine della valutazioneb
avrà ancora il valore3
ea
avrà il valore6
;printf("%d - % d - %d\n", a, b, j)
: saranno stampati i valori: pera
6
, perb
3
e perj
3
;j = ++b
: al termine della valutazioneb
conterrà il valore4
così comej
;b < a++ +10
: al termine della valutazioneb
avrà ancora il valore4
ea
avrà il valore7
;printf("%d - % d - %d\n", a, b, j)
: saranno stampati i valori: pera
7
, perb
4
e perj
4
. In più il ciclo si interromperà a causa dell’istruzionebreak
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.
- Inizializza la variabile
a = 1
. - Controlla se
a <= 10
e se lo è va al punto 3, altrimenti va al punto 6. - 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. - Esegue l’istruzione
printf
di stampa del valore dia
. - Incrementa
a
e va al punto 2. - Esce dal ciclo.
Per la sequenza del while
avremo invece quanto
segue.
- Controlla se
a <= 10
e se lo è va al punto 2, altrimenti va al punto 5. - Controlla se
a == 5
e se lo è non va al punto 3, incrementaa
di1
altrimenti il ciclo diventa infinito e poi va al punto 1. Se, viceversa,a == 5
è falsa, allora va al punto 3. - Esegue l’istruzione
printf
di stampa del valore dia
. - Incrementa
a
e va al punto 1. - 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.