goto
è quasi universalmente scoraggiato. Utilizzare questa affermazione è sempre utile?
Questo argomento è stato discusso più volte su Stack Overflow e Chris Gillum ha riepilogato i possibili usi di goto
:
Cleanly exiting a function
Often in a function, you may allocate resources and need to exit in multiple places. Programmers can simplify their code by putting the resource cleanup code at the end of the function all all "exit points" of the function would goto the cleanup label. This way, you don't have to write cleanup code at every "exit point" of the function.
Exiting nested loops
If you're in a nested loop and need to break out of all loops, a goto can make this much cleaner and simpler than break statements and if-checks.
Low-level performance improvements
This is only valid in perf-critical code, but goto statements execute very quickly and can give you a boost when moving through a function. This is a double-edged sword, however, because a compiler typically cannot optimize code that contains gotos.
Direi, come molti sostengono, che in tutti questi casi l'uso di goto
è usato come mezzo per uscire da un angolo in cui ci si è codificati ed è generalmente un sintomo di codice che potrebbe essere refactored.
I costrutti del flusso di controllo di livello superiore tendono a corrispondere ai concetti nel dominio del problema. An if / else è una decisione basata su alcune condizioni. Un loop dice di eseguire più volte un'azione. Anche una frase di separazione dice "lo stavamo facendo ripetutamente, ma ora dobbiamo fermarci".
Un'istruzione goto, d'altra parte, tende a corrispondere a un concetto nel programma in esecuzione, non nel dominio del problema. Dice di continuare l'esecuzione in un punto specificato nel programma . Qualcuno che legge il codice deve inferire che cosa significhi rispetto al dominio del problema.
Naturalmente tutti i costrutti di livello superiore possono essere definiti in termini di gotos e rami condizionali semplici. Ciò non significa che siano semplicemente goto sotto mentite spoglie. Pensa a loro come restricted gotos - e sono le restrizioni che li rendono utili. Un'istruzione break è implementata come un salto alla fine del ciclo di chiusura, ma è meglio pensarla come operativa sul loop nel suo complesso.
A parità di tutti gli altri, il codice la cui struttura riflette quella del dominio del problema tende a essere più facile da leggere e mantenere.
Non ci sono casi in cui una dichiarazione goto è assolutamente necessaria (c'è un teorema in questo senso), ma ci sono casi in cui può essere la soluzione meno cattiva. Questi casi variano da lingua a lingua, a seconda dei costrutti di livello superiore supportati dalla lingua.
In C, ad esempio, credo che ci siano tre scenari di base in cui un goto è appropriato.
D'altra parte, una macchina a stati finiti espliciti può anche essere implementata con un'istruzione switch all'interno di un loop. Questo ha il vantaggio che ogni stato inizia nello stesso punto nel codice, che può essere utile per il debug, ad esempio.
L'uso principale di un goto in un linguaggio ragionevolmente moderno (uno che supporta if / else e loop) è di simulare un costrutto di flusso di controllo che manca nella lingua.
Sicuramente dipende dal linguaggio di programmazione. Il motivo principale per cui goto
è stato controverso è a causa dei suoi effetti negativi che sorgono quando il compilatore ti permette di usarlo troppo liberamente. Ad esempio, possono sorgere problemi se ti consente di utilizzare goto
in modo tale che ora puoi accedere a una variabile non inizializzata o, peggio, a saltare in un altro metodo e ad interferire con lo stack di chiamate. Dovrebbe essere la responsabilità del compilatore di non consentire un flusso di controllo assurdo.
Java ha tentato di "risolvere" questo problema disabilitando completamente goto
. Tuttavia, Java ti consente di usare return
all'interno di un blocco finally
e quindi causare un'eccezione di ingerenza accidentale. Lo stesso problema è ancora lì: il compilatore non sta facendo il suo lavoro. La rimozione di goto
dalla lingua non l'ha corretto.
In C #, goto
è sicuro come break
, continue
, try/catch/finally
e return
. Non ti permette di usare variabili non inizializzate, non ti lascia saltare da un blocco finale, ecc. Il compilatore si lamenterà. Questo perché risolve il problema reale , che è come ho detto un flusso di controllo assurdo. goto
non cancella magicamente l'analisi delle assegnazioni definite e altri controlli del compilatore ragionevoli.
Sì. Quando i tuoi loop sono nidificati a diversi livelli, goto
è il modo solo di uscire elegantemente da un loop interno. L'altra opzione è impostare un flag e uscire da ogni loop se quel flag soddisfa una condizione. Questo è davvero brutto e piuttosto incline agli errori. In questi casi, goto
è semplicemente migliore.
Ovviamente, l'istruzione break
etichettata da Java fa la stessa cosa, ma senza consentire di passare a un punto arbitrario nel codice, che risolve il problema ordinatamente senza consentire le cose che rendono goto
male.
La maggior parte dello scoraggiamento arriva da una sorta di "religione" creata intorno a Dio Djikstra che era avvincente nei primi anni '60 del suo potere indiscriminato di:
Questo non ha più nulla a che fare con la dichiarazione goto
delle lingue moderne, la cui esistenza è semplicemente dovuta a supportare la creazione di strutture di codice diverse da quelle fornite dalla lingua.
In particolare il primo punto principale sopra è più permesso e il secondo è pulito (se tu goto
su un blocco lo stack è srindicato correttamente e tutti i distruttori corretti hanno chiamato)
Puoi fare riferimento a questa risposta per avere un'idea di come anche il codice che non utilizza goto possa essere illeggibile. Il problema non è goto stesso, ma il cattivo uso di esso.
Posso scrivere un intero programma senza usare if
, solo for
.
Certo, non sarà ben leggibile, un aspetto goffo e inutilmente complicato.
Ma il problema non è for
. Sono io.
Cose come break
, continue
, throw
, bool needed=true; while(needed) {...}
, ecc. notano più di masquerade goto
per sfuggire alle scimitarre degli zeloti di Djikstrarian, che -50 anni dopo l'invenzione dei moderni laguages- vogliono ancora i loro prigionieri. Hanno dimenticato ciò di cui parlava Djikstra, ricordano solo il titolo del suo biglietto (GOTO considera dannoso, e non era nemmeno il suo titolo onw: è stato modificato dall'editore) e incolpano e colpiscono, bash e incolpano ogni controt avendo quelli 4 lettera inserita in sequenza.
È il 2011: è giunto il momento di capire che goto
ha deciso di occuparsi dell'istruzione GOTO
di cui Djikstra era avvincente.
Il goto dispari qui o là, a patto che sia locale a una funzione, raramente nuoce significativamente alla leggibilità. Spesso ne trae beneficio richiamando l'attenzione sul fatto che c'è qualcosa di insolito in questo codice che richiede l'uso di una struttura di controllo piuttosto rara.
Se i gotos (locali) stanno danneggiando in modo significativo la leggibilità, di solito è un segno che la funzione che contiene il goto è diventata troppo complessa.
L'ultimo goto che ho inserito in un pezzo di codice C è stato quello di costruire un paio di loop ad incastro. Non si adatta alla normale definizione di "goto" accettabile, ma la funzione è risultata significativamente più piccola e chiara come risultato. Per evitare il goto avrebbe richiesto una violazione particolarmente disordinata di DRY.
Penso che l'intera questione sia stata un caso di abbaiare dall'albero sbagliato.
GOTO in quanto tale non mi sembra problematico, ma piuttosto è molto spesso un sintomo di un vero peccato: spaghetti code.
Se GOTO causa un grande incrocio di linee di controllo del flusso, allora è un periodo negativo. Se non attraversa linee di controllo del flusso è innocuo. Nella zona grigia in mezzo abbiamo cose come i salvataggi di loop, ci sono ancora alcune lingue che non hanno aggiunto costrutti che coprono tutti i casi grigi legittimi.
L'unico caso in cui mi sono trovato effettivamente a usarlo in molti anni è il caso del ciclo in cui il punto di decisione si trova nel mezzo del ciclo. Ti rimane il codice duplicato, una bandiera o GOTO. Trovo che la soluzione GOTO sia la migliore delle tre. Non c'è incrocio di linee di controllo del flusso qui, è innocuo.
Sì, il goto può essere utilizzato per avvantaggiare l'esperienza dello sviluppatore: link
Tuttavia, proprio come con qualsiasi strumento potente (puntatori, eredità multipla, ecc.), si deve essere disciplinati usandolo. L'esempio fornito nel collegamento utilizza PHP, che limita l'uso del costrutto goto alla stessa funzione / metodo e disabilita la possibilità di passare a un nuovo blocco di controllo (ad esempio, istruzione loop, switch, ecc.)
Dipende dalla lingua. È ancora ampiamente usato nella programmazione Cobol, per esempio. Ho anche lavorato su un dispositivo Barionet 50, il cui linguaggio di programmazione del firmware è uno dei primi dialetti BASIC che ovviamente richiede l'utilizzo di Goto.
Direi di no. Se trovi la necessità di utilizzare GOTO, scommetto che è necessario riprogettare il codice.
goto
può essere utile quando si porta il codice assembler legacy a C. Nella prima istanza, una conversione istruzione-per-istruzioni in C, usando goto
come sostituzione per l'istruzione branch
dell'assemblatore, può abilitare molto porting rapido.
Direi di no. Dovrebbero essere sempre sostituiti con uno strumento più specifico del problema che il goto viene utilizzato per risolvere (a meno che non sia disponibile nella tua lingua). Ad esempio, le istruzioni di interruzione e le eccezioni risolvono i problemi precedentemente risolti da goto dell'escaping di loop e della gestione degli errori.
Leggi altre domande sui tag programming-practices goto