Come altri hanno già detto, il problema non è con goto
se stesso; il problema è con come le persone usano goto
e come può rendere il codice più difficile da capire e mantenere.
Supponi il seguente frammento di codice:
i = 4;
label: printf( "%d\n", i );
Per quale valore viene stampato i
? Quando viene stampato? Fino a quando non si tiene conto di ogni istanza di goto label
nella propria funzione, non si può sapere. La semplice presenza di quell'etichetta distrugge la tua capacità di eseguire il debug del codice con una semplice ispezione. Per piccole funzioni con uno o due rami, non c'è molto problema. Per funzioni non piccole ...
All'inizio degli anni '90 ci è stato dato un mucchio di codice C che guidava un display grafico 3d e ci diceva di farlo correre più veloce. E 'stato solo circa 5000 righe di codice, ma tutto di esso era in main
, e l'autore ha usato circa 15 o giù di lì goto
s ramificazione in entrambe le direzioni. Inizialmente, questo era un codice errato, ma la presenza di quelli goto
peggiorava le cose. Il mio collega ha impiegato circa 2 settimane per risolvere il flusso di controllo. Ancora meglio, questi goto
risultati hanno prodotto un codice così strettamente accoppiato con se stesso che non abbiamo potuto apportare modifiche senza rompere qualcosa.
Abbiamo provato a compilare con l'ottimizzazione di livello 1, e il compilatore ha mangiato tutta la RAM disponibile, quindi tutti gli swap disponibili, e poi ha preso il panico nel sistema (che probabilmente non aveva nulla a che fare con gli goto
stessi, ma mi piace buttare lì quell'aneddoto).
Alla fine, abbiamo offerto al cliente due opzioni: riscriviamo tutto da zero o acquistiamo hardware più veloce.
Hanno comprato hardware più veloce.
Regole di Bode per l'utilizzo di goto
:
- Diramazione solo in avanti;
- Non bypassare le strutture di controllo (cioè non ramificarsi nel corpo di un'istruzione
if
o for
o while
);
- Non utilizzare
goto
al posto di una struttura di controllo
Ci sono casi in cui a goto
è la risposta giusta, ma sono rari (uscire da un ciclo profondamente annidato riguarda l'unico posto in cui lo userei).
MODIFICARE
Espandendo su quest'ultima affermazione, ecco uno dei pochi casi d'uso validi per goto
. Supponiamo di avere la seguente funzione:
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
for ( i = 0; i < N; i ++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
for ( j = 0; j < M; j++ )
{
arr[i][j] = malloc( sizeof *arr[i][j] * P );
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
return arr;
}
Ora, abbiamo un problema: cosa succede se una delle malloc
chiamate non riesce a metà? È improbabile che un evento del genere, non vogliamo restituire un array parzialmente allocato, né vogliamo semplicemente uscire dalla funzione con un errore; vogliamo ripulire dopo noi stessi e deallocare qualsiasi memoria parzialmente allocata. In una lingua che genera un'eccezione su un'allocazione errata, è abbastanza semplice: basta scrivere un gestore di eccezioni per liberare ciò che è già stato allocato.
In C, non hai una gestione strutturata delle eccezioni; devi controllare il valore di ritorno di ogni malloc
chiamata e intraprendere le azioni appropriate.
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( i = 0; i < N; i ++ )
{
if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
goto cleanup_1;
for ( j = 0; j < M; j++ )
{
if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
goto cleanup_2;
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
}
goto done;
cleanup_2:
// We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
while ( j-- )
free( arr[i][j] );
free( arr[i] );
// fall through
cleanup_1:
// We failed while allocating arr[i]; free up all previously allocated arr[i][j]
while ( i-- )
{
for ( j = 0; j < M; j++ )
free( arr[i][j] );
free( arr[i] );
}
free( arr );
arr = NULL;
done:
return arr;
}
Possiamo farlo senza usare goto
? Certo che possiamo - richiede solo un po 'di contabilità extra (e, in pratica, questo è il percorso che prenderei). Ma se stai cercando luoghi in cui l'utilizzo di a goto
non è immediatamente un segno di cattiva pratica o design, questo è uno dei pochi.