Rozumiem, że oprócz przełamywania pętli zagnieżdżonych w pętlach; goto
oświadczenie omijane i drwił jako błąd skłonnej stylu programowania, aby nigdy nie być użyte.
Alt Text: „Neal Stephenson uważa, że fajnie jest nazywać jego etykiety„ dengo ””.
Zobacz oryginalny komiks na stronie: http://xkcd.com/292/
Ponieważ nauczyłem się tego wcześnie; Naprawdę nie mam wglądu ani doświadczenia na temat tego, do jakiego rodzaju błędów goto
faktycznie prowadzi. O czym tu mówimy:
- Niestabilność?
- Nieusuwalny lub nieczytelny kod?
- Luki w zabezpieczeniach?
- Coś zupełnie innego?
Do jakiego rodzaju błędów faktycznie prowadzą instrukcje „goto”? Czy są jakieś historycznie znaczące przykłady?
Odpowiedzi:
Oto sposób spojrzenia na to, czego jeszcze nie widziałem w innych odpowiedziach.
Chodzi o zakres. Jednym z głównych filarów dobrej praktyki programowania jest utrzymanie ścisłego zasięgu. Potrzebujesz ścisłego zasięgu, ponieważ brakuje ci mentalnej zdolności do nadzorowania i rozumienia więcej niż tylko kilku logicznych kroków. Tworzysz więc małe bloki (z których każdy staje się „jedną rzeczą”), a następnie budujesz z nich, aby tworzyć większe bloki, które stają się jedną rzeczą i tak dalej. Dzięki temu wszystko jest łatwe do zarządzania i zrozumiałe.
Goto skutecznie zwiększa zakres twojej logiki do całego programu. To z pewnością pokona Twój mózg w przypadku wszystkich programów oprócz najmniejszych obejmujących tylko kilka linii.
Więc nie będziesz już w stanie stwierdzić, czy popełniłeś błąd, ponieważ jest po prostu zbyt wiele do zaakceptowania i sprawdzenia swojej biednej małej główki. To jest prawdziwy problem, błędy są tylko prawdopodobnym rezultatem.
źródło
Nie chodzi o to, że
goto
samo w sobie jest złe. (W końcu każda instrukcja skoku w komputerze jest goto.) Problem polega na tym, że istnieje ludzki styl programowania, który poprzedza programowanie strukturalne, co można nazwać programowaniem „schematu blokowego”.W programowaniu schematów blokowych (którego nauczyli się ludzie z mojego pokolenia i który był używany w programie księżycowym Apollo) tworzysz schemat z blokami do wykonywania instrukcji i diamentami do podejmowania decyzji, i możesz połączyć je liniami, które biegną wszędzie. (Tak zwany „kod spaghetti”.)
Problem z kodem spaghetti polega na tym, że ty, programista, „wiesz”, że miał rację, ale jak możesz go udowodnić sobie lub komukolwiek innemu? W rzeczywistości może to mieć potencjalne złe zachowanie, a twoja wiedza, że zawsze jest poprawna, może być błędna.
Wraz z programowaniem strukturalnym wprowadzono bloki początkowe, na razie, jeśli-jeszcze, i tak dalej. Zaletą tego było to, że nadal można w nich zrobić cokolwiek, ale jeśli będziesz w ogóle ostrożny, możesz mieć pewność, że kod jest poprawny.
Oczywiście ludzie wciąż mogą pisać kod spaghetti, nawet bez niego
goto
. Powszechną metodą jest napisanie awhile(...) switch( iState ){...
, w którym różne przypadki ustawiająiState
różne wartości. W rzeczywistości w C lub C ++ można napisać makro, aby to zrobić, i nazwać toGOTO
, więc powiedzenie, że nie używasz,goto
jest różnicą bez różnicy.Jako przykład tego, w jaki sposób sprawdzanie kodu może wykluczać nieograniczone
goto
, dawno temu natknąłem się na strukturę sterowania przydatną w dynamicznie zmieniających się interfejsach użytkownika. Nazwałem to wykonaniem różnicowym . Jest w pełni Turing uniwersalny, ale jego poprawność dowód zależy od czystego programowania strukturalnego - brakgoto
,return
,continue
,break
, lub wyjątki.źródło
Dlaczego jest niebezpieczny?
goto
sam w sobie nie powoduje niestabilności. Mimo około 100 000goto
s jądro Linuksa wciąż jest modelem stabilności.goto
sam w sobie nie powinien powodować luk w zabezpieczeniach. W językach, jednak niektóre zmieszanie go ztry
/catch
wyjątku bloki zarządzania może prowadzić do luk w sposób opisany w niniejszym zaleceniu CERT . Kompilatory głównego nurtu C ++ sygnalizują i zapobiegają takim błędom, ale niestety starsze lub bardziej egzotyczne kompilatory tego nie robią.goto
powoduje nieczytelny i niemożliwy do utrzymania kod. Nazywa się to również kodem spaghetti , ponieważ podobnie jak na talerzu do spaghetti bardzo trudno jest podążać za kontrolą, gdy jest zbyt wiele goto.Nawet jeśli uda ci się uniknąć kodu spaghetti i jeśli użyjesz tylko kilku gotów, nadal ułatwiają one takie błędy, jak i wyciekanie zasobów:
goto
oświadczenia przerywasz ten prosty przepływ i przełamujesz oczekiwania. Na przykład możesz nie zauważyć, że nadal musisz zwolnić zasoby.goto
w różnych miejscach może wysłać cię do jednego celu goto. Więc nie jest oczywiste, aby wiedzieć na pewno, w jakim jesteś stanie, gdy dotrzesz do tego miejsca. Ryzyko dokonywania błędnych / bezpodstawnych założeń jest zatem dość duże.Dodatkowe informacje i cytaty:
E.Dijkstra napisał wczesny esej na ten temat już w 1968 roku: „ Przejdź do oświadczenia uznanego za szkodliwe ”
Brian.W. Kernighan & Dennis.M. Ritchie napisał w języku programowania C:
James Gosling i Henry McGilton napisali w swojej białej księdze z 1995 r. Dotyczącej środowiska Java :
Bjarne Stroustrup definiuje goto w swoim glosariuszu w następujących zachęcających terminach:
Kiedy można go użyć?
Podobnie jak K&R, nie jestem dogmatyczny w kwestii gotos. Przyznaję, że są sytuacje, w których goto może ułatwić sobie życie.
Zazwyczaj w C goto pozwala na wielopoziomowe wyjście z pętli lub obsługę błędów wymagającą dotarcia do odpowiedniego punktu wyjścia, który uwalnia / odblokowuje wszystkie zasoby, które zostały przydzielone do tej pory (wielokrotna alokacja w sekwencji oznacza wiele etykiet). W tym artykule opisano różne zastosowania goto w jądrze systemu Linux.
Osobiście wolę tego unikać i za 10 lat C użyłem maksymalnie 10 gotów. Wolę używać zagnieżdżonych
if
, które moim zdaniem są bardziej czytelne. Gdyby to prowadziło do zbyt głębokiego zagnieżdżenia, wolałbym albo rozłożyć moją funkcję na mniejsze części, albo użyć wskaźnika logicznego w kaskadzie. Dzisiejsze kompilatory optymalizujące są wystarczająco sprytne, aby wygenerować prawie ten sam kod niż ten sam kodgoto
.Zastosowanie goto w dużej mierze zależy od języka:
W C ++ prawidłowe użycie RAII powoduje, że kompilator automatycznie niszczy obiekty, które wykraczają poza zakres, tak że zasoby / blokada i tak zostaną wyczyszczone, i nie ma już potrzeby używania goto.
W Javie nie ma potrzeby goto (patrz autora cytat Javy powyżej i to doskonałe przepełnieniem stosu odpowiedź ): garbage collector że czyści bałagan
break
,continue
itry
/catch
obsługa wyjątków obejmują wszystkie sprawy, gdziegoto
mogłyby być pomocne, ale w bezpieczniejszym i lepiej sposób. Popularność Javy dowodzi, że oświadczenia goto można uniknąć w nowoczesnym języku.Powiększ słynną lukę w zabezpieczeniach SSL goto fail
Ważna informacja: z uwagi na zaciętą dyskusję w komentarzach chcę wyjaśnić, że nie udaję, że jedyną przyczyną tego błędu jest instrukcja goto. Nie udaję, że bez goto nie byłoby błędu. Chcę tylko pokazać, że goto może być związane z poważnym błędem.
Nie wiem, z iloma poważnymi błędami wiąże się
goto
historia programowania: szczegóły często nie są przekazywane. Był jednak znany błąd Apple SSL, który osłabił bezpieczeństwo iOS. Oświadczenie, które doprowadziło do tego błędu, było błędnegoto
.Niektórzy twierdzą, że główną przyczyną błędu nie była sama instrukcja goto, ale niewłaściwe kopiowanie / wklejanie, wprowadzające w błąd wcięcie, brak nawiasów klamrowych wokół bloku warunkowego lub może nawyki pracy programisty. Nie mogę potwierdzić żadnego z nich: wszystkie te argumenty są prawdopodobnymi hipotezami i interpretacją. Nikt tak naprawdę nie wie. ( tymczasem hipoteza scalenia, która poszła nie tak, jak ktoś sugerował w komentarzach, wydaje się być bardzo dobrym kandydatem, biorąc pod uwagę inne niespójności wcięcia w tej samej funkcji ).
Jedynym obiektywnym faktem jest to, że duplikat
goto
doprowadził do przedwczesnego wyjścia z funkcji. Patrząc na kod, jedyną inną instrukcją, która mogła wywołać ten sam efekt, byłby zwrot.Błąd działa
SSLEncodeSignedServerKeyExchange()
w tym pliku :Rzeczywiście, nawiasy klamrowe wokół bloku warunkowego mogłyby zapobiec
błędowi : doprowadziłoby to albo do błędu składniowego przy kompilacji (a zatem do korekty), albo do zbędnego nieszkodliwego goto. Nawiasem mówiąc, GCC 6 byłby w stanie wykryć te błędy dzięki opcjonalnemu ostrzeżeniu, aby wykryć niespójne wcięcie.
Ale przede wszystkim tych wszystkich gotów można było uniknąć dzięki bardziej uporządkowanemu kodowi. Więc goto jest przynajmniej pośrednią przyczyną tego błędu. Istnieją co najmniej dwa różne sposoby, aby tego uniknąć:
Podejście 1: jeśli klauzula lub zagnieżdżone
if
sZamiast sekwencyjnego testowania wielu warunków pod kątem błędu i za każdym razem, gdy wysyłany jest do
fail
etykiety w przypadku problemu, można było zdecydować się na wykonanie operacji kryptograficznych w stanieif
, który zrobiłby to tylko, gdyby nie było złego warunku wstępnego:Podejście 2: użyj akumulatora błędów
Podejście to opiera się na fakcie, że prawie wszystkie instrukcje tutaj wywołują jakąś funkcję w celu ustawienia
err
kodu błędu i wykonują resztę kodu tylko wtedy, gdyerr
wynosi 0 (tj. Funkcja wykonana bez błędu). Dobrą bezpieczną i czytelną alternatywą jest:Tutaj nie ma jednego goto: nie ma ryzyka, aby szybko przejść do punktu wyjścia z awarii. A wizualnie łatwo byłoby zauważyć niedopasowaną linię lub zapomnianą
ok &&
.Ta konstrukcja jest bardziej zwarta. Opiera się na fakcie, że w C druga część logicznego i (
&&
) jest oceniana tylko wtedy, gdy pierwsza część jest prawdziwa. W rzeczywistości asembler wygenerowany przez kompilator optymalizacyjny jest prawie równoważny oryginalnemu kodowi z gotos: Optymalizator bardzo dobrze wykrywa łańcuch warunków i generuje kod, który przy pierwszej niezerowej wartości zwrotnej przeskakuje na koniec ( dowód online ).Można nawet przewidzieć kontrolę spójności na końcu funkcji, która może podczas fazy testowania zidentyfikować niedopasowania między flagą ok a kodem błędu.
Błędy takie jak
==0
przypadkowo zastąpione!=0
błędami lub logicznymi błędami złącza można łatwo wykryć podczas fazy debugowania.Jak powiedziano: nie udaję, że alternatywne konstrukcje uniknęłyby jakiegokolwiek błędu. Chcę tylko powiedzieć, że mogły sprawić, że błąd będzie trudniejszy do wystąpienia.
źródło
goto
nawiasówif
. Ale jeśli sugerujesz, że unikaniegoto
uczyni twój kod odpornym na działanie losowo powielonych linii, mam dla ciebie złe wieści.if
instrukcja? Dobre czasy. :)Słynny artykuł Dijkstry został napisany w czasie, gdy niektóre języki programowania były w stanie tworzyć podprogramy posiadające wiele punktów wejścia i wyjścia. Innymi słowy, możesz dosłownie wskoczyć na środek funkcji i wyskoczyć z dowolnego miejsca w funkcji, bez faktycznego wywoływania tej funkcji lub powrotu z niej w konwencjonalny sposób. To nadal dotyczy języka asemblera. Nikt nigdy nie twierdzi, że takie podejście jest lepsze od ustrukturyzowanej metody pisania oprogramowania, z której teraz korzystamy.
W większości współczesnych języków programowania funkcje są ściśle określone za pomocą jednego wejścia i jednego punktu wyjścia. Punkt wejścia to miejsce, w którym określasz parametry funkcji i wywołujesz ją, a punkt wyjścia to miejsce, w którym zwracasz wynikową wartość i kontynuujesz wykonywanie zgodnie z instrukcją po oryginalnym wywołaniu funkcji.
W ramach tej funkcji powinieneś być w stanie robić, co chcesz, w granicach rozsądku. Jeśli wstawienie goto lub dwóch w funkcji czyni je bardziej zrozumiałym lub poprawia prędkość, dlaczego nie? Sedno funkcji polega na zsekwencjonowaniu odrobiny jasno określonej funkcjonalności, abyś nie musiał myśleć o tym, jak działa ona wewnętrznie. Po napisaniu wystarczy go użyć.
I tak, możesz mieć wiele instrukcji return wewnątrz funkcji; zawsze jest jedno miejsce w odpowiedniej funkcji, z którego powracasz (w zasadzie tylna strona funkcji). To wcale nie jest to samo, co wyskakiwanie z funkcji z goto, zanim będzie ona miała szansę powrócić.
Więc tak naprawdę nie chodzi o używanie goto. Chodzi o unikanie ich nadużyć. Wszyscy zgadzają się, że możesz stworzyć strasznie skomplikowany program za pomocą gotos, ale możesz zrobić to samo, nadużywając funkcji (o wiele łatwiej jest nadużywać gotos).
Co jest warte, odkąd skończyłem z programów BASIC w stylu numeru linii do programowania strukturalnego przy użyciu języków Pascal i nawiasów klamrowych, nigdy nie musiałem używać goto. Kusiło mnie, aby użyć jednego z nich, aby zrobić wczesne wyjście z zagnieżdżonej pętli (w językach, które nie obsługują wielopoziomowego wczesnego wyjścia z pętli), ale zwykle mogę znaleźć inny sposób, który jest czystszy.
źródło
goto
Kiedy pisałem programy BASIC jako dziecko, używałem instrukcji jako prostego sposobu uzyskiwania pętli for i while (Commodore 64 BASIC nie miał pętli while i byłem zbyt niedojrzały, aby nauczyć się właściwej składni i użycia pętli for ). Mój kod był często trywialny, ale wszelkie błędy w pętli można było natychmiast przypisać mojemu użyciu goto.Teraz używam głównie Pythona, języka programowania wysokiego poziomu, który stwierdził, że nie potrzebuje goto.
Kiedy Edsger Dijkstra ogłosił w 1968 r., Że „Goto uważa się za szkodliwe”, nie podał kilku przykładów, w których powiązane błędy można obwinić za goto, ale stwierdził, że nie jest
goto
to konieczne w przypadku języków wyższego poziomu i że należy tego unikać na korzyść tego, co rozważamy dzisiaj normalny przepływ sterowania: pętle i warunki warunkowe. Jego słowa:Prawdopodobnie miał mnóstwo przykładów błędów za każdym razem, gdy debugował
goto
w nim kod . Ale jego artykuł był uogólnionym oświadczeniem o stanowisku popartym dowodem, którygoto
był zbędny w przypadku języków wyższego poziomu. Uogólniony błąd polega na tym, że możesz nie mieć możliwości statycznej analizy kwestionowanego kodu.Kod jest znacznie trudniejszy do analizy statycznej za pomocą
goto
instrukcji, szczególnie jeśli cofniesz się w przepływie kontrolnym (co kiedyś robiłem) lub do jakiejś niepowiązanej sekcji kodu. Kod może i został napisany w ten sposób. Zrobiono to, aby zoptymalizować bardzo ograniczone zasoby komputerowe, a tym samym architekturę maszyn.Przykład apokryficzny
Był program blackjacka, który opiekun stwierdził, że jest dość elegancko zoptymalizowany, ale również niemożliwy do „naprawienia” ze względu na naturę kodu. Został zaprogramowany w kodzie maszynowym, który jest w dużej mierze zależny od gotos - więc myślę, że ta historia jest całkiem trafna. To najlepszy kanoniczny przykład, jaki mogę wymyślić.
Kontrprzykład
Jednak źródło C CPython (najczęstsza i referencyjna implementacja Pythona) używa
goto
instrukcji z doskonałym skutkiem. Służą do omijania nieistotnego przepływu sterowania wewnątrz funkcji, aby dotrzeć do końca funkcji, dzięki czemu funkcje są bardziej wydajne bez utraty czytelności. Jest to zgodne z jedną z idei programowania strukturalnego - posiadania jednego punktu wyjścia dla funkcji.Dla przypomnienia, ten kontrprzykład jest dość łatwy do analizy statycznej.
źródło
Kiedy wigwam był obecną sztuką architektoniczną, jego budowniczowie mogli bez wątpienia udzielić ci praktycznych porad dotyczących budowy wigwamów, jak pozwolić dymowi na ucieczkę i tak dalej. Na szczęście dzisiejsi budowniczowie prawdopodobnie zapomnieli większość z tych rad.
Kiedy dyliżans był obecną sztuką transportową, jego kierowcy mogli bez wątpienia udzielić ci praktycznych porad dotyczących koni dyliżansów, jak bronić się przed rozbójnikami i tak dalej. Na szczęście dzisiejsi kierowcy również mogą zapomnieć o większości tych rad.
Kiedy karty perforowane były aktualną sztuką programowania, ich praktycy mogli również udzielić ci praktycznych porad dotyczących organizacji kart, sposobu numerowania wyciągów i tak dalej. Nie jestem pewien, czy ta rada jest dziś bardzo aktualna.
Czy jesteś wystarczająco stary, aby znać wyrażenie „numerować oświadczenia” ? Nie musisz tego wiedzieć, ale jeśli tego nie wiesz, to nie znasz historycznego kontekstu, w którym ostrzeżenie przeciwko
goto
było zasadniczo istotne.Ostrzeżenie przed
goto
dzisiaj nie jest zbyt istotne. Dzięki podstawowemu szkoleniu w pętlach while / for i wywoływaniu funkcji nie będziesz nawet myśleć o wydawaniugoto
zbyt często. Kiedy myślisz o tym, prawdopodobnie masz powód, więc śmiało.Ale czy
goto
nie można nadużywać tego oświadczenia?Odpowiedź: pewnie, może być nadużywane, ale jego nadużycie jest dość drobnym problemem w inżynierii oprogramowania w porównaniu do znacznie bardziej powszechnych błędów, takich jak używanie zmiennej, w której będzie służyć stała, lub takie jak programowanie typu „wklej i wklej” (w przeciwnym razie znany jako zaniedbanie refaktoryzacji). Wątpię, czy jesteś w niebezpieczeństwie. O ile nie używasz
longjmp
lub nie przenosisz kontroli do odległego kodu, jeśli myślisz o użyciugoto
lub chcesz po prostu wypróbować dla zabawy, śmiało. Wydobrzejesz.Możesz zauważyć brak ostatnich opowiadań grozy, w których
goto
gra czarny charakter. Większość lub wszystkie te historie wydają się mieć 30 lub 40 lat. Stajesz na solidnym gruncie, jeśli uważasz te historie za w większości przestarzałe.źródło
Aby dodać jedną rzecz do innych doskonałych odpowiedzi, za pomocą
goto
instrukcji może być trudno powiedzieć dokładnie, jak dostałeś się do dowolnego miejsca w programie. Możesz wiedzieć, że wystąpił wyjątek w określonym wierszu, ale jeśligoto
w kodzie jest taki kod, nie ma sposobu, aby stwierdzić, które instrukcje wykonane w wyniku tego wyjątku powodują stan bez przeszukiwania całego programu. Nie ma stosu wywołań ani przepływu wizualnego. Możliwe, że istnieje instrukcja oddalona o 1000 linii, która wprawia cię w zły stan, wykonując agoto
do linii, która zgłosiła wyjątek.źródło
On Error Goto errh
, możeszResume
spróbować ponownie,Resume Next
pominąć linię obrażającą iErl
dowiedzieć się, która to linia (jeśli używasz numerów linii).Resume
ponownie uruchomi tę funkcję od początku iResume Next
pominie wszystko w funkcji, która jeszcze tego nie zrobiła.On Error Goto 0
i ręcznie debugować.Resume
jest przeznaczony do użycia po sprawdzeniuErr.Number
i dokonaniu niezbędnych korekt.goto
działa tylko z oznaczonymi liniami, które wydają się być zastąpione przez oznaczone pętle .Goto jest trudniejszy do zrozumienia dla ludzi niż inne formy kontroli przepływu.
Programowanie poprawnego kodu jest trudne. Pisanie poprawnych programów jest trudne, ustalenie, czy programy są poprawne, jest trudne, udowodnienie, że programy są poprawne, jest trudne.
Sprawienie, by kod niejasno robił, co chcesz, jest łatwe w porównaniu ze wszystkim innym na temat programowania.
goto rozwiązuje niektóre programy, uzyskując kod do robienia tego, co chcesz. Nie ułatwia to sprawdzania poprawności, a często robią to alternatywy.
Istnieją style programowania, w których goto jest właściwym rozwiązaniem. Istnieją nawet style programowania, w których jego zły bliźniak, comefrom, jest właściwym rozwiązaniem. W obu tych przypadkach należy zachować szczególną ostrożność, aby upewnić się, że używasz go w zrozumiały sposób, oraz wiele ręcznych kontroli, czy nie robisz czegoś trudnego w celu zapewnienia poprawności.
Na przykład istnieje funkcja niektórych języków zwana coroutines. Coroutines to nici bezgwintowe; stan wykonania bez wątku, na którym można go uruchomić. Możesz poprosić ich o wykonanie, a oni mogą uruchomić część siebie, a następnie zawiesić się, przekazując kontrolę przepływu zwrotnego.
„Płytkie” coroutine w językach bez obsługi coroutine (jak C ++ przed C ++ 20 i C) są możliwe przy użyciu kombinacji gotos i ręcznego zarządzania stanem. „Głębokie” coroutines można wykonać za pomocą funkcji
setjmp
ilongjmp
C.Są przypadki, w których coroutines są tak przydatne, że warto je pisać ręcznie i ostrożnie.
W przypadku C ++ są one na tyle przydatne, że rozszerzają język, aby je obsługiwać. W
goto
s i instrukcja zarządzania stan jest ukryty za zerowy koszt warstwy abstrakcji, co pozwala programistom pisać je bez trudności z konieczności udowodnienia ich bałagan Goto,void**
s, ręcznej budowy / destrukcji państwa, etc jest poprawna.Goto ukrywa się za abstrakcją wyższego poziomu, taką jak
while
lubfor
lubif
lubswitch
. Te abstrakcje wyższego poziomu łatwiej jest udowodnić i sprawdzić.Jeśli brakuje jakiegoś języka (jak w niektórych współczesnych językach brakuje coroutine), albo kulejąc wraz ze wzorem, który nie pasuje do problemu, albo używając goto, staje się twoją alternatywą.
Łatwo jest sprawić, by komputer wykonywał niejasno to, co chcesz w typowych przypadkach . Pisanie niezawodnie solidnego kodu jest trudne . Goto pomaga pierwszemu znacznie bardziej niż drugiemu; stąd „został uznany za szkodliwy”, ponieważ jest to znak powierzchownie „działającego” kodu z głębokimi i trudnymi do wyśledzenia błędami. Przy wystarczającym wysiłku możesz nadal tworzyć niezawodnie niezawodny kod z „goto”, więc trzymanie się reguły absolutnej jest błędne; ale z reguły jest dobry.
źródło
longjmp
jest dozwolone tylko w C dla odwijania stosu z powrotem dosetjmp
funkcji nadrzędnej. (Jak przełamywanie wielu poziomów zagnieżdżania, ale dla wywołań funkcji zamiast pętli).longjjmp
do kontekstu z funkcjisetjmp
wykonanej w funkcji, która została zwrócona, nie jest legalna (i podejrzewam, że w większości przypadków nie będzie działać). Nie znam wspólnych procedur, ale z twojego opisu wspólnej procedury jako podobnej do wątku przestrzeni użytkownika, myślę, że potrzebowałby własnego stosu, a także kontekstu zapisanych rejestrów ilongjmp
daje tylko rejestr - oszczędzanie, a nie osobny stos.Spójrz na ten kod z http://www-personal.umich.edu/~axe/research/Software/CC/CC2/TourExec1.1.f.html, który faktycznie był częścią dużej symulacji Dylematu Więźnia. Jeśli widziałeś stary kod FORTRAN lub BASIC, zdasz sobie sprawę, że nie jest to takie niezwykłe.
Jest tutaj wiele problemów, które znacznie wykraczają poza oświadczenie GOTO; Szczerze uważam, że oświadczenie GOTO było trochę kozłem ofiarnym. Ale przepływ sterowania nie jest tutaj absolutnie jasny, a kod jest mieszany razem w taki sposób, że bardzo niejasne jest to, co się dzieje. Nawet bez dodawania komentarzy lub używania lepszych nazw zmiennych, zmiana na strukturę bloków bez GOTO znacznie ułatwiłaby czytanie i śledzenie.
źródło
Jedną z zasad programowalnego programowania jest hermetyzacja . Chodzi o to, że łączysz się z modułem / procedurą / podprogramem / komponentem / obiektem za pomocą zdefiniowanego interfejsu i tylko tego interfejsu, a wyniki będą przewidywalne (zakładając, że urządzenie zostało skutecznie przetestowane).
W ramach jednej jednostki kodu obowiązuje ta sama zasada. Jeśli konsekwentnie stosujesz zasady programowania strukturalnego lub programowania obiektowego, nie będziesz:
Niektóre z bardziej powszechnych objawów tych błędów przetwarzania obejmują wycieki pamięci, gromadzenie pamięci, przepełnienie wskaźnika, awarie, niekompletne rekordy danych, wyjątki dodawania / chg / usuwania rekordów, błędy stron pamięci i tak dalej.
Obserwowane przez użytkownika przejawy tych problemów obejmują blokowanie interfejsu użytkownika, stopniowe zmniejszanie wydajności, niekompletne zapisy danych, niemożność inicjowania lub wykonywania transakcji, uszkodzone dane, przerwy w sieci, przerwy w dostawie prądu, awarie systemów wbudowanych (z powodu utraty kontroli nad pociskami rakietowymi) utrata zdolności śledzenia i kontroli w systemach kontroli ruchu lotniczego), awarie czasomierzy itp. Katalog jest bardzo obszerny.
źródło
goto
ani razu. :)goto jest niebezpieczne, ponieważ zwykle stosuje się go tam, gdzie nie jest potrzebny. Używanie czegokolwiek, co nie jest potrzebne, jest niebezpieczne, ale przede wszystkim musisz . Jeśli google znajdziesz wokół wielu błędów spowodowanych przez goto, nie jest to sam w sobie powód, aby go nie używać (błędy zawsze występują, gdy używasz funkcji językowych, ponieważ jest to nieodłączne w programowaniu), ale niektóre z nich są wyraźnie wysoce powiązane do goto użytkowania.
Powody używania / nieużywania goto :
Jeśli potrzebujesz pętli, musisz użyć
while
lubfor
.Jeśli potrzebujesz wykonać skok warunkowy, użyj
if/then/else
Jeśli potrzebujesz procedury, wywołaj funkcję / metodę.
Jeśli chcesz wyjść z funkcji, po prostu
return
.Mogę liczyć na palce w miejscach, w których widziałem
goto
używane i używane prawidłowo.W libKTX jest funkcja, która ma następujący kod
Teraz w tym miejscu
goto
jest przydatne, ponieważ językiem jest C:Ten przypadek użycia jest przydatny, ponieważ w C nie mamy klas, więc najprostszym sposobem na oczyszczenie jest użycie
goto
.Gdybyśmy mieli ten sam kod w C ++, nie ma już potrzeby używania goto:
Do jakiego rodzaju błędów może to prowadzić? Byle co. Nieskończone pętle, niewłaściwa kolejność wykonywania, śruba stosu ..
Udokumentowana sprawa to luka w zabezpieczeniach SSL, która pozwoliła na ataki typu Man in the środkowego spowodowane niewłaściwym użyciem goto: oto artykuł
To błąd w literówce, ale przez jakiś czas był niezauważany, jeśli kod byłby skonstruowany w inny sposób, poprawnie przetestowany taki błąd nie mógłby być możliwy.
źródło