Istruzione Goto in Linguaggio C
L'istruzione break
e continue
sono istruzioni di salto, ossia particolari istruzioni di controllo del flusso che permettono di effettuare un salto da un punto del codice ad un altro.
Tuttavia, entrambe le istruzioni sono limitate:
- Nel caso dell'istruzione
break
, il salto avviene solo dall'interno di un ciclo o di unoswitch
all'istruzione immediatamente successiva ad esso; - Nel caso dell'istruzione
continue
, il salto avviene solo all'interno di un ciclo, saltando l'iterazione corrente e passando alla successiva.
L'istruzione goto
, invece, permette di saltare a qualsiasi istruzione del codice a patto che questa sia un'istruzione dotata di un'etichetta.
Sebbene l'uso dell'istruzione goto
sia generalmente sconsigliato ed è un residuo di linguaggi di programmazione più vecchi, essa è ancora presente in C e può essere utilizzata per scopi particolari. In questa lezione, vedremo come funziona l'istruzione goto
in C e come utilizzarla correttamente.
Etichette
Prima di tutto, è importante capire cosa è un'etichetta di un'istruzione in linguaggio C.
In C possiamo assegnare un identificatore a qualsiasi istruzione del codice, creando così un'etichetta. Un'etichetta è un identificatore seguito da due punti (:
) e può essere posizionata prima di qualsiasi istruzione.
La sintassi per definire un'etichetta è la seguente:
nome_etichetta: istruzione;
Ad esempio, possiamo definire un'etichetta chiamata inizio
prima di un'istruzione printf
:
#include <stdio.h>
int main() {
inizio: // Etichetta chiamata "inizio"
printf("Questo è l'inizio del programma.\n");
return 0;
}
In questo esempio, l'etichetta inizio
è definita prima dell'istruzione printf
.
Un'istruzione può avere più etichette, basta che siano univoche all'interno del codice. Ad esempio:
#include <stdio.h>
int main() {
inizio: // Etichetta chiamata "inizio"
inizio2: // Etichetta chiamata "inizio2"
printf("Questo è l'inizio del programma.\n");
return 0;
}
Etichetta (Label)
In C, un'etichetta è un identificatore seguito da due punti (:
) che permette di riferirsi a un'istruzione specifica nel codice.
La sintassi per definire un'etichetta è:
nome_etichetta: istruzione;
Le etichette in C, a parte il fatto di assegnare un nome a un'istruzione, non hanno alcun effetto sul flusso del programma. Esse però hanno essenzialmente due scopi principali:
-
Vengono utilizzate per i costrutti
switch
:Le etichette sono utilizzate per identificare i casi all'interno di un costrutto
switch
, come abbiamo già visto, permettendo al programma di saltare direttamente al caso corrispondente. -
Vengono utilizzate con l'istruzione
goto
:Le etichette sono utilizzate per definire i punti di salto per l'istruzione
goto
, permettendo al programma di saltare a un'istruzione specifica nel codice.
Avendo già visto il primo caso, ora vediamo come utilizzare le etichette con l'istruzione goto
.
Istruzione goto
L'istruzione goto
permette di saltare a un'etichetta specifica nel codice, eseguendo l'istruzione che segue l'etichetta. La sintassi per utilizzare l'istruzione goto
è la seguente:
goto nome_etichetta;
Ad esempio, possiamo utilizzare l'istruzione goto
per saltare a un'etichetta chiamata fine
:
#include <stdio.h>
int main() {
printf("Inizio del programma.\n");
goto fine; // Salta all'etichetta "fine"
// Questa istruzione non verrà eseguita!
printf("Questa istruzione non verrà eseguita.\n");
fine: // Etichetta chiamata "fine"
printf("Fine del programma.\n");
return 0;
}
In questo esempio, l'istruzione goto fine;
salta direttamente all'etichetta fine
, facendo sì che l'istruzione printf("Questa istruzione non verrà eseguita.\n");
non venga eseguita.
Istruzione goto
L'istruzione goto
in C permette di saltare a un'etichetta specifica nel codice, eseguendo l'istruzione che segue l'etichetta.
La sintassi per utilizzare l'istruzione goto
è:
goto nome_etichetta;
Come si può notare, l'istruzione goto
permette di saltare a qualsiasi punto del codice che abbia un'etichetta definita, rendendo il flusso del programma meno lineare e più difficile da seguire. Pertanto, l'uso dell'istruzione goto
è generalmente sconsigliato, poiché può rendere il codice meno leggibile e più difficile da mantenere.
Ad esempio, prendiamo in considerazione il seguente codice:
#include <stdio.h>
int main() {
int i = 0;
for (i = 0; i < 5; i++) {
printf("Iterazione %d\n", i);
}
return 0;
}
Questo codice esegue un ciclo for
che stampa le iterazioni da 0 a 4. Si tratta di un codice semplice e soprattutto facile da leggere e comprendere.
Potremmo riscrivere lo stesso codice utilizzando l'istruzione goto
, ma il risultato sarebbe molto meno chiaro:
#include <stdio.h>
int main() {
int i = 0;
inizio: // Etichetta chiamata "inizio"
if (i < 5) {
printf("Iterazione %d\n", i);
i++;
goto inizio; // Salta all'etichetta "inizio"
}
return 0;
}
Si tratta a tutti gli effetti di un'istruzione di salto "non strutturata", residuo di linguaggi di programmazione più vecchi.
Evitare l'uso dell'istruzione goto
Si sconsiglia fortemente l'uso dell'istruzione goto
in C, poiché può rendere il codice meno leggibile e più difficile da mantenere. È preferibile utilizzare strutture di controllo del flusso come if
, for
, while
e switch
per gestire il flusso del programma in modo più chiaro e strutturato.
Detto questo, bisogna tenere a mente che il linguaggio C mette comunque dei vincoli all'uso dell'istruzione goto
:
Vincoli dell'istruzione goto
- L'istruzione
goto
può saltare solo a etichette che si trovano nella stessa funzione in cui è stata dichiarata; - In C99 e versioni successive, l'istruzione
goto
non può bypassare la dichiarazione di un array a lunghezza variabile.
Quando utilizzare l'istruzione goto
Sebbene l'uso dell'istruzione goto
sia generalmente sconsigliato, ci sono alcuni casi in cui può comunque essere utile.
Il primo caso riguarda l'uscita da cicli o blocchi di codice annidati. Ad esempio, prendiamo in considerazione il seguente codice che utilizza un ciclo annidato:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i == 2 && j == 2) {
/* ... */
break;
}
printf("i: %d, j: %d\n", i, j);
}
}
In questo caso, l'istruzione break
interrompe solo il ciclo interno, ma non quello esterno.
Se volessimo interrompere entrambi i cicli, potremmo utilizzare l'istruzione goto
per saltare a un'etichetta che si trova al di fuori di entrambi i cicli:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i == 2 && j == 2) {
goto fine; // Salta all'etichetta "fine"
}
printf("i: %d, j: %d\n", i, j);
}
}
fine: // Etichetta chiamata "fine"
printf("Uscito dai cicli.\n");
In questo caso, l'istruzione goto
permette di uscire da entrambi i cicli in modo semplice e diretto.
Un altro caso in cui l'istruzione goto
può essere utile è quando si desidera gestire errori o condizioni particolari in modo centralizzato. Ad esempio, si potrebbe utilizzare goto
per saltare a un'etichetta di pulizia alla fine di una funzione, evitando la duplicazione del codice di pulizia in diversi punti della funzione.
#include <stdio.h>
int main() {
FILE *file = fopen("file.txt", "r");
if (file == NULL) {
printf("Errore nell'apertura del file.\n");
return 1;
}
// Altre operazioni sul file...
if (/* condizione di errore */) {
goto pulizia; // Salta all'etichetta "pulizia"
}
// Altre operazioni...
pulizia: // Etichetta chiamata "pulizia"
fclose(file); // Chiude il file
printf("File chiuso correttamente.\n");
return 0;
}
Ancora non abbiamo studiato i file in C, ma l'idea è che l'istruzione goto
permette di saltare direttamente alla sezione di pulizia del codice, evitando la duplicazione del codice di chiusura del file in caso di errori.
Un ultimo caso in cui l'istruzione goto
può essere utile è quando si desidera avere il controllo massimo sulle prestazioni del codice, ad esempio in situazioni di programmazione a basso livello o in contesti di ottimizzazione delle prestazioni. Tuttavia, questi casi sono rari e richiedono una buona comprensione del codice, delle sue implicazioni e soprattutto dell'architettura sottostante sia in termini di processore che di sistema operativo.
In generale, l'uso dell'istruzione goto
dovrebbe essere limitato a situazioni in cui le alternative più strutturate non sono praticabili o non offrono la stessa flessibilità. Tuttavia, è importante ricordare che l'uso eccessivo di goto
può portare a codice difficile da leggere e mantenere, quindi è sempre consigliabile valutare attentamente se è davvero necessario utilizzarlo.