Corso di C con Linux

Strutture di controllo - Parte VI

 

Con questa lezione concludiamo la nostra carrellata sulle strutture di controllo fornite dal linguaggio C.
In particolare ci occuperemo di due strumenti che consentono di ramificare il flusso di esecuzione di un programma, ovvero la tanto discussa (vedremo perché) istruzione goto e le label ("etichette").

Una label è sintatticamente uguale ad una variabile ed è seguita dal carattere due punti, con una forma del tipo:

mia_etichetta:
istruzioni;
...


Per convenzione, l'etichetta è allineata a sinistra, in una riga di codice a sé stante.
La funzione delle etichette è di permettere al programmatore di indicare una riga di codice alla quale passare durante l'esecuzione, quando viene incontrata la parola chiave goto.
Infatti l'istruzione goto ha la seguente forma:

goto mia_etichetta;

Vediamo un semplice esempio che ne illustra l'utilizzo:

/*
* File lez39_1.c
*/
#include <stdio.h>

int main()
{
int i = 2;
int j = 100;
int x;

for (; i < 10; i++)
while (--j)
{
x = i * j;
if (x < 185)
goto fuori;
printf("i * j = %d\n", x);
}

fuori:
printf("Sono fuori!\n");

return 0;
}

Eseguendolo otterremo:

i * j = 198
i * j = 196
i * j = 194
i * j = 192
i * j = 190
i * j = 188
i * j = 186
Sono fuori!

Il funzionamento dei cicli for e while dovrebbe essere chiaro.
Quello che risulta evidente è che l'utilizzo di goto provoca, una volta verificata la condizione dell'if, l'uscita da entrambi i cicli annidati e l'esecuzione della istruzione seguente all'etichetta fuori.
Se proviamo a mettere break al posto di goto, l'output del programma è ben diverso.
Infatti, l'istruzione break provoca l'uscita dal solo ciclo while, mentre il corpo del costrutto for viene eseguito fino alla fine (cioè finché i è minore di 10).
Notiamo che in entrambi i casi l'istruzione successiva all'etichetta viene eseguita comunque.

L'esempio riportato, seppur banale, mostra uno dei pochi casi che può giustificare l'utilizzo di goto, ovvero l'uscita da cicli annidati al verificarsi di particolari eventi o errori.
Non è, infatti, consigliabile ricorrere all'istruzione goto, sia perché il suo utilizzo rende il codice più difficile da leggere e da mantenere (soprattutto se l'etichetta cui fa riferimento si trova in una porzione molto distante), sia perché qualsiasi segmento di codice contenente un goto può sempre essere scritto in una forma equivalente che non ne fa uso.
Riscriviamo l'esempio precedente senza utilizzare goto:

/*
* File lez39_2.c
*/
#include <stdio.h>
#define FALSE 0
#define TRUE 1

int main()
{
int i = 2;
int j = 100;
int x;
short posso_uscire = FALSE;
for (; i < 10; i++)
{
if (posso_uscire)
break;
while (--j)
{
x = i * j;
if (x >= 185)
printf("i * j = %d\n", x);
else
{
posso_uscire = TRUE;
break;
}
}
}
printf("Sono fuori!\n");

return 0;
}

In questo caso abbiamo dovuto introdurre una nuova variabile e qualche controllo in più, ma il risultato è il medesimo del caso precedente.

Molti sviluppatori, soprattutto quelli provenienti dall'Assembler, hanno abusato di questa istruzione, che in passato è stata al centro di numerose discussioni.
Attualmente sembrano tutti d'accordo nell'affermare che è preferibile non ricorrere ad essa, tant'è gli autori di uno dei moderni linguaggi di programmazione più usati, ovvero Java, pur prendendo molto dalla sintassi del C++, e quindi anche del C, hanno preferito non includerlo fra le istruzioni disponibili.

Con questa lezione abbiamo terminato l'argomento delle strutture di controllo; nella prossima approfondiremo gli array, cui abbiamo accennato nella lezione 31.

 

Torna all'indice Generale del corso di Corso di C con Linux di Software Planet