Come altri hanno già detto, il problema non è con goto
stesso; il problema è con il modo in cui le persone usano goto
e come può rendere più difficile la comprensione e la manutenzione del codice.
Assumi il seguente snippet di codice:
i = 4;
label: printf( "%d\n", i );
Quale valore viene stampato per i
? Quando viene stampato? Fino a quando non conti l'istanza ogni di goto label
nella tua funzione, non puoi 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 è un grosso problema. Per le funzioni non piccole ...
All'inizio degli anni '90 ci fu data una pila di codice C che guidava un display grafico 3d e gli diceva di farlo funzionare più velocemente. Si trattava solo di circa 5000 righe di codice, ma tutte di esso era in main
, e l'autore usava circa 15 branching di goto
s in entrambe le direzioni. Questo è stato il codice cattivo per cominciare, ma la presenza di quei goto
s lo ha reso molto peggio. Ho impiegato il mio collega circa 2 settimane per chiarire il flusso del controllo. Ancora meglio, quei goto
s hanno portato a un codice così strettamente associato a se stesso che noi non potremmo apportare modifiche senza rompere qualcosa.
Abbiamo provato la compilazione con l'ottimizzazione di livello 1, e il compilatore ha mangiato tutta la RAM disponibile, quindi tutto lo scambio disponibile, e poi ha fatto il panico del sistema (che probabilmente non aveva nulla a che fare con il goto
s, ma mi piace buttarlo aneddoto là fuori).
Alla fine, abbiamo dato al cliente due opzioni: riscriviamo il tutto da zero o acquistiamo hardware più veloce.
Hanno comprato hardware più veloce.
Regole di Bode per l'utilizzo di goto
:
- Solo inoltro;
- Non bypassare le strutture di controllo (ad esempio, non diramarti 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 un goto
è la risposta giusta, ma sono rari (uscire da un ciclo profondamente annidato riguarda l'unico posto in cui lo userei).
Modifica
Espansione dell'ultima istruzione, ecco uno dei pochi casi di utilizzo 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 chiamate malloc
fallisce a metà? Per quanto improbabile possa essere un evento, non vogliamo restituire un array parzialmente assegnato, né vogliamo semplicemente salvare la funzione con un errore; vogliamo ripulire 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 assegnato.
In C, non hai una gestione strutturata delle eccezioni; devi controllare il valore di ritorno di ciascuna chiamata malloc
e prendere l'azione appropriata.
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à in più (e, in pratica, questa è la strada che prenderò). Ma, se stai cercando luoghi in cui l'utilizzo di goto
non è immediatamente un segno di cattiva pratica o progettazione, questo è uno dei pochi.