Mergi la

goto (din engleză  go to  - "go to") - un operator de salt necondiționat (săriți la un anumit punct din program, indicat printr-un număr de linie sau o etichetă) în unele limbaje de programare . În unele limbi, operatorul de ramură necondiționat poate avea un nume diferit (de exemplu, jmpîn limbaje de asamblare ).

Funcționalitate

De regulă, o instrucțiune gotoconstă din două părți: instrucțiunea în sine și o etichetă care indică punctul de salt țintă din program: . Eticheta, în funcție de regulile limbajului, poate fi fie un număr (ca, de exemplu, în BASIC clasic), fie un identificator al limbajului de programare utilizat. Pentru etichetele de identificare, eticheta este de obicei plasată înaintea instrucțiunii la care trebuie sărită și separată de aceasta prin două puncte ( ). goto меткаметка:

Acțiunea instrucțiunii de salt este aceea că după executarea acesteia, următoarele instrucțiuni de program care intră în text imediat după etichetă (până la următoarea instrucțiune de salt, ramură sau buclă) vor fi executate. Pentru limbajele de mașină , instrucțiunea de salt se copiază în registrul procesorului care conține adresa următoarei instrucțiuni de executat, adresa instrucțiunii marcată cu etichetă.

Distribuție

Operatorul gotoeste disponibil în limbi precum Fortran , Algol , Cobol , BASIC , C și C++ , C# , D , Pascal , Perl , Ada , PHP și multe altele. De asemenea, este prezent în toate limbile de asamblare (de obicei sub numele jmp, jumpsau bra(din filiala engleză   - ramură)). Libertatea de utilizare variază de la o limbă la alta. Dacă în asamblori sau limbaje precum Fortran poate fi folosit în mod arbitrar (este permis să se transfere controlul în interiorul unei ramuri a unui operator condiționat sau în corpul unei bucle sau al unei proceduri), atunci în limbajele de nivel superior se utilizează limitat: de regulă, este interzisă transferul controlului între diferite proceduri și funcții folosind în interiorul blocului de instrucțiuni selectat, între ramurile instrucțiunii condiționate și instrucțiunii cu alegere multiplă. gotogoto

gotoabsent în unele limbi de nivel înalt (de exemplu, Forth ). Pascal goto nu a fost inclus inițial, dar lipsa instrumentelor lingvistice disponibile l-a forțat pe Niklaus Wirth să-l adauge. În limbile sale ulterioare, Wirth a abandonat încă goto: acest operator nu este în Modulul-2 , nici în Oberon și Componenta Pascal . Java are un cuvânt rezervat goto , dar nu are nicio funcție - nu există un operator de salt necondiționat în limbaj (totuși, saltul poate fi făcut [1] ). În același timp, etichetele au fost păstrate în limbaj - pot fi folosite pentru a ieși din bucle imbricate cu operatorii breakși continue.

Critica

Operatorul gotoîn limbi de nivel înalt este ținta criticilor, deoarece utilizarea excesivă a acestuia duce la crearea unui „ cod spaghete ” imposibil de citit . Acest punct de vedere a fost reflectat pentru prima dată în articolul lui Edsger Dijkstra „Argumente împotriva declarației GOTO”, [2] care a observat că calitatea unui cod de program este invers proporțională cu numărul de declarații gotodin acesta. Articolul a devenit cunoscut pe scară largă atât în ​​rândul teoreticienilor, cât și al practicienilor în programare, în urma căruia opiniile cu privire la utilizarea operatorului gotoau fost revizuite semnificativ. În următoarea sa lucrare, Dijkstra a fundamentat faptul că pentru cod fără gotoeste mult mai ușor să se verifice corectitudinea formală a .

Codul C gotoeste dificil de format, deoarece poate rupe ierarhia execuției ( paradigma de programare structurată ) și, prin urmare, indentările concepute pentru a afișa structura programului pot să nu fie întotdeauna setate corect. gotointerferează de asemenea cu optimizările compilatorului ale structurilor de control. [3]

Unele utilizări gotopot crea probleme cu logica de execuție a programului:

Argumentele împotriva operatorului gotos-au dovedit a fi atât de grave încât în ​​programarea structurată au început să fie considerate ca fiind extrem de nedorite. Acest lucru s-a reflectat în proiectarea noilor limbaje de programare. De exemplu, a gotofost interzis în Java și Ruby . Într-un număr de limbi moderne, este încă lăsat din motive de eficiență în acele cazuri rare când utilizarea este gotojustificată. Așadar, s- gotoa păstrat în Ada  , una dintre cele mai gândite limbi în materie de arhitectură din istorie. [4] Cu toate acestea, în acele limbi moderne de nivel înalt în care acest operator a fost păstrat, utilizarea sa, de regulă, este supusă unor restricții severe care împiedică utilizarea celor mai periculoase metode de aplicare: de exemplu, este interzisă trecerea controlului din exteriorul buclei, procedurii sau funcției din interior. Standardul limbajului C++ interzice ocolirea inițializării variabilei cu goto.

Se dovedește formal ( teorema Boehm-Jacopini ) că aplicația gotoeste opțională, adică nu există un astfel de program cu gotocare să nu poată fi rescris fără el cu funcționalitate deplină (cu toate acestea, eventual cu o pierdere de eficiență).

Utilizare justificată

În programarea practică, utilizarea gotoeste uneori considerată acceptabilă atunci când alte instrumente de limbaj nu implementează sau nu implementează eficient funcționalitatea dorită.

Principalul criteriu de aplicabilitate gotoeste neîncălcarea paradigmei de programare utilizată (în exemplele de mai jos, aceasta este programarea structurată ), în caz contrar rezultatul este plin de tot felul de efecte secundare și erori greu de găsit.

Ieșirea din bucle imbricate

Unele limbi nu au operatori de terminare a buclei sau se referă doar la bucla imbricată în care se află (de exemplu, breakîn continueC). Folosirea gotopentru a ieși dintr-o dată din mai multe bucle imbricate în acest caz simplifică foarte mult codul programului, eliminând necesitatea de a folosi variabile de tip auxiliar și instrucțiuni condiționale .

Alte soluții la această problemă sunt să puneți bucle imbricate într-o procedură separată și să utilizați o instrucțiune de ieșire a procedurii, iar în limbile cu suport pentru excepții  , să aruncați o excepție al cărei mâner este situat în afara buclelor. Cu toate acestea, astfel de soluții sunt mai puțin eficiente din cauza supraîncărcării de implementare, mai ales dacă secțiunea corespunzătoare de cod este apelată de mai multe ori.

Exemplu în C++:

int matrice [ n ][ m ]; valoare int ; ... pentru ( int i = 0 ; i < n ; ++ i ) pentru ( int j = 0 ; j < m ; ++ j ) dacă ( matrice [ i ][ j ] == valoare ) { printf ( "valoarea %d găsită în celulă (%d,%d) \n " , valoare , i , j ); //acționează dacă s-a găsit merge la end_loop ; } printf ( "valoarea %d nu a fost găsită \n " , valoare ); //acționează dacă nu a fost găsit end_loop : ;

O modalitate simplă de a scăpa de goto ea este să creați o variabilă de tip flag suplimentară care semnalează ieșirea din bucla exterioară (după ieșirea din bucla interioară cu break ) și să ocoliți blocul de cod care este executat atunci când valoarea nu este găsită.

Fără a schimba structura codului, problema este rezolvată dacă comanda break(sau echivalentul acesteia) vă permite să ieșiți din mai multe blocuri imbricate simultan, ca în Java sau Ada . Exemplu Java:

int [][] matrice ; valoare int ; ... exterior : { pentru ( int i = 0 ; i < n ; i ++ ) pentru ( int j = 0 ; j < m ; j ++ ) dacă ( matrice [ i ][ j ] == valoare ) { Sistem . afară . println ( "valoare " + valoare + " găsită în celulă (" + i + "," + j + ")" ); sparge exterioară ; } Sistem . afară . println ( "valoare" + valoare + "nu a fost găsit" ); }

Cel mai elegant mod de a ieși dintr-o buclă imbricată este PHP [5] . După comandă break, puteți specifica numărul de cicluri de părăsit:

pentru ( $i = 0 ; $i < $Imax ; ++ $i ) { // ... pentru ( $j = 0 ; $j < $Jmax ; ++ $j ) { // ... dacă ( stare ) break 2 ; // ... } // ... }

Gestionarea erorilor

Dacă limbajul nu are facilități de gestionare a excepțiilor , atunci instrucțiunea goto poate fi folosită pentru a întrerupe execuția „normală” a codului și a sări la codul final pentru a elibera memoria ocupată și alte acțiuni finale. Exemplu în limbajul C:

int fn ( int * presult ) { int sts = 0 ; TYPE entity , another_entity = NULL ; TYPE2 entity2 = NULL ; dacă ( ! ( entitate = create_entity () ) ) { sts = ERROR_CODE1 ; du- te la ieșire0 ; } dacă ( ! face_ceva ( entitate ) ) { sts = ERROR_CODE2 ; du- te la exit1 ; } if ( condiție ) { dacă ( ! ( entity2 = create_other_entity () ) ) { sts = ERROR_CODE3 ; du- te la exit1 ; } if ( ( * presult = do_another_thing ( entity2 ) == NEGATIVE ) { sts = ERROR_CODE4 ; du- te la ieșirea2 ; } } altfel { if ( ( * presult = face_something_special ( entitate ) == NEGATIVE ) { sts = ERROR_CODE5 ; du- te la exit2 ; } } exit2 : if ( entity2 ) distruge_other_entity ( entity2 ); exit1 : distruge_entitate ( entitate ); exit0 : intoarce ochi ; }

Fără goto , un astfel de cod ar fi aglomerat în mod inutil cu multe instrucțiuni condiționale suplimentare if.

Generarea codului

O altă utilizare validă a unui salt necondiționat este codul care este generat automat, cum ar fi lexeri și analizatorii generați de instrumente software. Astfel, codul generat de utilitățile yacc , lex , bison este plin de comenzi goto, dar acest cod, în principiu, nu este destinat percepției și editării umane, iar corectitudinea sa este în întregime determinată de corectitudinea instrumentului care îl creează.

În limbaje de asamblare

Este un operator necesar și este folosit peste tot. De-a lungul anilor, nu există nicio schimbare în intensitatea utilizării sale. Mai mult decât atât, majoritatea platformelor de calcul acceptă, de asemenea, un instrument atât de eficient ca o ramură necondiționată indexată, permițând un timp minim (mai multe instrucțiuni ale mașinii, până la unul) pentru a transfera controlul către una dintre numeroasele subrutine, a cărei alegere este determinată de conținut. a unuia dintre registrele procesorului. Totuși, pentru aceasta, începuturile (punctele de intrare) ale tuturor rutinelor din acest set trebuie plasate în RAM cu un pas fix. Deoarece acesta din urmă este dificil de implementat prin intermediul limbajelor de nivel înalt, un salt necondiționat indexat nu este de obicei disponibil în ele, este înlocuit cu o căutare mai puțin eficientă în tabel.

Note

  1. com.sun.org.apache.bcel.internal.generic: clasă publică: GOTO . Preluat la 6 august 2010. Arhivat din original la 5 februarie 2010.
  2. E. Dijkstra. Argumente împotriva declarației goto . Consultat la 26 februarie 2007. Arhivat din original pe 23 februarie 2007.
  3. Donald Knuth. Programare structurată cu acces la Declarații Arhivate din original pe 24 august 2009. 1974
  4. Code Complete: A Practical Handbook of Software Construction Arhivat la 2 iunie 2017 la Wayback Machine Redmond: Microsoft Press, 1993. 880 p.
  5. Continuarea ciclului și ieșirea din acesta . Consultat la 4 iunie 2015. Arhivat din original pe 22 mai 2015.

Link -uri