Warum Goto und Gosub nur mit Vorsicht gebrauchen.

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Benutzeravatar
PAMKKKKK
Beiträge: 321
Registriert: 21.04.2005 22:08
Wohnort: Braunschweig
Kontaktdaten:

Warum Goto und Gosub nur mit Vorsicht gebrauchen.

Beitrag von PAMKKKKK »

Warum man Goto und Gosub nicht benutzen soll, Sie aber trotzdem manchmal gebraucht werden.

Damals in den Anfangstagen der Basicprogrammierung war die Sprache Basic sehr Zeilenorientiert.
Man hat vor jeder Zeile eine Zeilenummer mitprogrammiert.
Diese Zeilennummer war dann wie eine Sprungmarke. Damit konnte man nun im Programm sagen das das Programm z.B. von Zeile 10 in die Zeile 30 Springen soll und da weitermachen soll.

Code: Alles auswählen

10 if Zahl = 1 then goto 30
20 print "Zahl ist nicht 1 !!"
30 print "Zahl ist 1!!"
Man hat die Zeilen in 10er Schritten Nummeriert um Später Zeilen einfügen zu können.

Code: Alles auswählen

10 if Zahl = 1 then goto 30
11 if Zahl = 2 then goto 31
20 print "Zahl ist nicht 1 und nicht 2!!"
30 print "Zahl ist 1!!"
31 print "Zahl ist 2!!"
Auch PureBasic bietet so etwas wie Sprungmarken an.
Eine Sprungmarke ist einfach ein Wort mit einem Doppelpunkt am ende.
z.B. SpringeHierHin:

Code: Alles auswählen

If OpenConsole()
  lZahl = 5

  Select lZahl
    Case 1
      Goto Melde1
    Case 2
      Goto Melde2
    Case 3
      Goto Melde3
    Case 4
      Goto Melde4
    Case 5
      Goto Melde5
    Case 6
      Goto Melde6
    Default
      Goto MeldeNix
  EndSelect

  Melde1:
  PrintN("Die Zahl ist 1!")

  Melde2:
  PrintN("Die Zahl ist 2!")

  Melde3:
  PrintN("Die Zahl ist 3!")

  Melde4:
  PrintN("Die Zahl ist 4!")

  Melde5:
  PrintN("Die Zahl ist 5!")
  Goto ProgrammEnde

  Melde6:
  PrintN("Die Zahl ist 6!")

  MeldeNix:
  PrintN("Die Zahl wurde nicht gefunden!")

  ProgrammEnde:
  PrintN("....druecke Enter.....")
  Input()
  CloseConsole()
  End
EndIf
Wenn man jetzt nun die Zeile lZahl = 5 in lZahl = 4 oder lZahl = 1 ändert und das Programm ausführt, wird einem schnell klar, das man auf den Programmverlauf nach der Sprungmarke sehr gut aufpassen muss.
Wenn man einen sehr langen Quelltext hat, wird es für den Programmierer sehr sehr schwer, das hin und her gehüpfe, kreuz und quer durch den Quelltext zu verfolgen.
Das nennt man in der Fachsprache "Spagetticode" weil es nur sehr schwer zu verfolgen ist.

Schnell passieren Fehler, die in einer Endlosschleife oder gar zum Programmabsturz führen.

Code: Alles auswählen

If OpenConsole()
Anfang:
  lZahl = 1

  Select lZahl
    Case 1
      Goto Melde1
    Case 2
      Goto Melde2
    Case 3
      Goto Melde3
    Case 4
      Goto Melde4
    Case 5
      Goto Melde5
    Case 6
      Goto Melde6
    Default
      Goto MeldeNix
  EndSelect

  Melde1:
  PrintN("Die Zahl ist 1!")

  Melde2:
  PrintN("Die Zahl ist 2!")

  Melde3:
  PrintN("Die Zahl ist 3!")

  Melde4:
  PrintN("Die Zahl ist 4!")

  Melde5:
  PrintN("Die Zahl ist 5!")
  Goto ProgrammEnde

  Melde6:
  PrintN("Die Zahl ist 6!")

  MeldeNix:
  PrintN("Die Zahl wurde nicht gefunden!")

  ProgrammEnde:
  PrintN("....druecke Enter.....")
  Input()
  Goto Anfang
  CloseConsole()
  End
EndIf
Bei Modernen Programmiersprachen, wie auch PureBasic, nutzt man Prozeduren oder Funktionen als Sprungmarken.
Diese Prozeduren oder Funktionen werden angesprungen, der dort enthaltene Code wird ausgeführt und das Programm geht automatisch an den Ort zurück von wo die Prozedur aufgerufen wurde.
Die Programmsteuerung ist also von vorne herein geregelt und leicht nachvollziehbar.

Code: Alles auswählen

Global lZahl1,lZahl2,lErgebnis

Procedure Rechne()
  lErgebnis = lZahl1 + lZahl2
EndProcedure

If OpenConsole()

  lZahl1 = 5
  lZahl2 = 6

  Rechne()

  PrintN("Ergebnis = " + Str(lErgebnis)) 
  PrintN("....druecke Enter.....")
  Input() 
EndIf
End
Deshalb sollte man die Proceduren nutzen, die auch noch mehrer andere vorteile haben in Bezug der Gültigkeit der Variablen und mehr.

Es gibt jedoch einen Vorteil den das Gespann aus Goto und Sprungmarke hat.
Eine Sprungmarke ist beim Programmablauf eine Speicheradresse in die das Programm sehr leicht springen kann ohne vorher zu suchen.
Das Gespann aus Goto und Sprungmarke ist also schneller als ein Sprung zu einer Prozedur und wieder zurück.
Bei sehr Zeitkritischen schnellen Operationen kann man Goto und Sprungmarke nutzen.

Wenn aber aus irgendwelchen Gründen die Sprungmarke (Speicheradresse) falsch oder Ungültig ist dann springt das Goto ins ungewisse!!!!!!!!!!



Gosub, Return und FakeReturn

Gosub ist eine Abkürzung für 'Go to sub routine', es ist also auch ein Goto das weiterentwickelt wurde. Nach Gosub muss auch eine Sprungmarke angegeben werden.
Ist im Programm ein Gosub, so merkt sich das Progamm im Stack die Speicheradresse, von der Stelle wo das Gosub steht (die Adresse wird auf den Stack gelegt, Stack = Spezieller Speicher der sich Sachen merkt).
Nun kann man später an diese stelle wieder zurückkehren, weil man sich die Adresse gemerkt hat.

Dann wird die Programmausführung ab der Sprungmarke weitergeführt, bis ein Return auftaucht. Return heißt soviel wie: Kehre zu der stelle zurück an der das Gosub stand (die stellen haben wir uns im Stack gemerkt).
Nach Return kann der Stack die gemerkte Adresse wieder „vergessen“.

Wenn also das Return erreicht wurde, wird die Programmausführung hinter dem aufrufenden Gosub fortgesetzt.
Genauso Funktionieren auch Proceduren! Proceduren sind aus mehreren gründen vorzuziehen!

Gosub ist aber nützlich, um einigermaßen strukturierten Code zu erstellen der in der Ausführung schnell ist.

Code: Alles auswählen

  
a = 1
  b = 2
  Gosub ComplexOperation
  PrintNumberN(a)
  End

  ComplexOperation:
    a = b*2+a*3+(a+b)
    a = a+a*a
  Return

Wenn Sie aus der Gosub-Routine mit dem Befehl Goto in einen anderen Programmteil außerhalb der Gosub-Routine springen möchten , müssen Sie FakeReturn benutzen, um ein Return zu simulieren, ohne es wirklich auszuführen (englisch Fake = gefälscht). Wenn Sie diesen Befehl nicht benutzen, wird Ihr Programm abstürzen. Diese Funktion sollte nutzlos sein, da ein ordentlich aufgebautes und strukturiertes Programm kein Goto benutzt.

Code: Alles auswählen

  
Gosub SubRoutine1

  SubRoutine1:
    ...
    If a = 10
      FakeReturn
      Goto Main_Loop
    EndIf
  Return
In einem Programm das nur mit Goto und Gosub Programmiert ist sind alle Variablen Global und somit unsicher vor versehentlichen Veränderungen!

Weil Goto und Gosub so viele Nachteile haben, hat man die Funktionen und Proceduren entwickelt.
Die Prozeduren sind also eine Weiterentwicklung.
Wer sich Programmiertechnisch weiter entwickeln will, nutzt Prozeduren.



Proceduren haben gegenüber Goto und Gosub folgende Vorteile:

* Der Programmablauf ist Leichter nachvollziehbar
* die Variablen in Prozeduren und Programm haben ordentlich definierte Gültigkeitsbereiche
* Prozeduren haben definierte Übergabe- und Rückgabeschnittstellen (so wie DLL Libraries)
* Ein Fehlsprung im Speicher ist mit Prozeduren durch die Überprüfung beim Kompelieren nicht Möglich

Goto und Gosub sind schneller….
Zuletzt geändert von PAMKKKKK am 17.10.2005 16:19, insgesamt 2-mal geändert.
Wir Schreiben ein PureBasic Buch.
Auch du kannst mitmachen!
http://www.purearea.net/pb/english/pure ... :Main_Page
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Schön erklärt!

Vorher habe ich auch mal in Basic und QBasic programmiert, da gabs noch keine Funktionen / Prozeduren.
Nur in JavaScript, als ich mit dieser Sprache anfing, musste ich mich an Funktionen bzw. Prozeduren gewöhnen, aber das war gar nicht schwer!
Man sollte meiner Meinung nach aber noch etwas erwähnen.

Code: Alles auswählen

Goto starte_hier

unter_programm1:
  text$ = "Beschriftung des Knopfes"
Return

starte_hier:

OpenWindow(0, 0, 0, 200, 150, #PB_Window_SystemMenu|#PB_Window_ScreenCentered, "Fenstertitel")
CreateGadgetList(WindowID(0))
  ;
  ;
  ; gaaaaanz viele Gadgets (Knöpfe, Eingabefelder, usw.)
  ;
  Gosub unter_programm1
  ButtonGadget(1, 10, 10, 100, 25, text$)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
End
Das Ganze ist nicht so kompliziert, wie es aussieht. Als erstes springt das Programm zur Marke (auch "Label" genannt) namens starte_hier. Dann erstellt es ein Fenster mit vielen Gadgets. Zum Schluss kommt noch ein Knopf, dessen Aufschrift in der Variablen text$ gespeichert ist. Diese Variable steht aber weiter oben im Programm. Also springt es eben schnell zum unter_programm1. Dort wird die Variable text$ mit dem String "Beschriftung des Knopfes" gefüllt. Dann gehts mit dem Return wieder zurück da, wo das Programm den kleinen Ausflug gestartet hat. Nun erstellt es den Knopf mit der Aufschrift, die in text$ gespeichert ist.

Hier nochmal das Gleiche, aber mit Prozeduren:

Code: Alles auswählen


Procedure. s unter_programm1()
  ProcedureReturn "Aufschrift des Knopfes"
EndProcedure

OpenWindow(0, 0, 0, 200, 150, #PB_Window_SystemMenu|#PB_Window_ScreenCentered, "Fenstertitel") 
CreateGadgetList(WindowID(0)) 
  ; 
  ; 
  ; gaaaaanz viele Gadgets (Knöpfe, Eingabefelder, usw.) 
  ; 
text$ = unter_programm1()
  ButtonGadget(1, 10, 10, 100, 25, text$)
Hier entfallen nicht nur die Befehle Gosub und Return, sondern auch die Labels. Ebenso wird nicht in der Prozedur unter_programm1() die Variable text$ mit "Aufchrift des Knopfes" gefüllt, sondern das geschieht 1 Zeile vor dem Knopf, da die Prozedur den String zurückgibt.

Beide Beispiele haben aber einen Nachteil, der noch erwähnt werden sollte. Und zwar wird das Programm an der Stelle vor dem Knopf so lange angehalten, bis die Variable ext$ gefüllt ist.

Man kann Prozeduren auch so aufrufen, dass sie ihre Arbeit erledigen, ohne, dass das Programm so lange angehalten wird. Nämlich mit

Code: Alles auswählen

CreateThread(@unter_programm1(), 0)
Diesen Vorteil hat man zusätzlich noch bei Prozeduren.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
PAMKKKKK
Beiträge: 321
Registriert: 21.04.2005 22:08
Wohnort: Braunschweig
Kontaktdaten:

Beitrag von PAMKKKKK »

@AND51
Weder beim Goto, Gosub noch bei Proceduren wird etwas angehalten!
Das Programm läuft einfach einen anderen Weg weiter.
Ader hälst du beim Fahren an, nur weil du in eine andere Strasse abbiegst?? :wink: :wink: :wink: :wink:
Wir Schreiben ein PureBasic Buch.
Auch du kannst mitmachen!
http://www.purearea.net/pb/english/pure ... :Main_Page
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Und es ist mir neu ... das QBasic keine Prozeduren kann ... im Editor heißen die nur SUB ... und Funktionen gibt es dort auch ... das einzige was anders ist, dass diese im Editor in einem eigenen Fenster angezeigt werden.

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
PAMKKKKK
Beiträge: 321
Registriert: 21.04.2005 22:08
Wohnort: Braunschweig
Kontaktdaten:

Beitrag von PAMKKKKK »

@AND51
Danke für deinen Vergleichsbeispiel Gosub und Proceduren.
Werde es im Wiki mit einbauen.

PureBasic Dokumentieren, mach mit! :allright:
http://www.purearea.net/pb/english/pure ... :Main_Page
Wir Schreiben ein PureBasic Buch.
Auch du kannst mitmachen!
http://www.purearea.net/pb/english/pure ... :Main_Page
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Weder beim Goto, Gosub noch bei Proceduren wird etwas angehalten!
Das Programm läuft einfach einen anderen Weg weiter.

Das stimmt. In dem Sinne wird nichts angehalten, ich habe mich falsch ausgedrückt.
Was ich meinte ist folgendes:
Ich habe gerade ein Programm, das erstellt ein Fenster mit Gadgets und soll gleichzeitig die verfügbaren IPs auf dem Computer ermitteln.
Wenn ich das mit den ersten beiden Beispielen realisieren würde, würde der letzte Knopf solange nicht erstellt, bis die IPs ermittelt wurden (die bei mir im unter_programm1 ermittelt werden).
Das meinte ich in diesem Sinne mit "Das Programm wird angehalten".

Möchte ich aber beide Vorgänge parallel (gleichzeitig) ablaufen lassen, könnte ich noch vor OpenWindow() die Prozedur unter_programm1 (wo ja die IPs ermittelt werden) mittels CreateThread() aufrufen.
Das bewirkt, dass die auf dem Computer verfügbaren IPs ermittelt werden, während ich das fenster mit den Gadgets erstellen lasse.
Das ist oben genau dasselbe: Noch während die Gadgets + fenster erstellt werden, wird schon die Variable text$ mit "beschriftung des Knopfes" abgefüllt.

Wenn man diese Technik nicht nur mit so kleinen popeligen Sachen wie hier anwendet, sondern mit umfangreicheren Code und außerdem öfter auf CreateThread() zurückgreift, kann man den Programmablauf beschleunigen. Das geht mit einfachem Aufrufen einer Prozedur oder unter Verwendung von Gosub+Return/Goto nicht.

Und es ist mir neu ... das QBasic keine Prozeduren kann ... im Editor heißen die nur SUB
Sorry, das stimmt. Es gibt diese besagten SUBs. QBasic kennt SUBs, nur soweit war ich in QB (QBasic) noch nicht. Ich glaube, in der gleichen Zeit, in der ich mit QB programmiert habe (und (ich schätze) 25% von QB gelernt habe), werde ich in PB schon mehr ((ich schätze wieder) 42%) kennen und können.

Danke für deinen Vergleichsbeispiel Gosub und Proceduren.
Bitte, bitte. Den habe ich von Anfängern für Anfänger geschrieben (bin ja selber noch "fortgeschrittener" Anfänger).

Werde es im Wiki mit einbauen.
Cool! Insgeheim, wenn ich ehrlich bin, habe ich das irgendwie gehofft <) . Wird man da auch irgendwie namentlich erwähnt? Meine Signatur AND reicht schon...
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Sylvia
verheiratet<br>1. PureGolf-Gewinner
Beiträge: 487
Registriert: 29.08.2004 09:42
Wohnort: Old Europe

Beitrag von Sylvia »

Da schau her...dieser Thread ist schon Steinalt und ich lese ihn jetzt
erst.

Pamkkkk,...ich will hier nicht wieder in das Thema "Goto/Gosub sinnvoll ?"
einsteigen, aber solche Aussagen wie von dir z.B.
...das hin und her gehüpfe, kreuz und quer durch den Quelltext zu verfolgen.
Das nennt man in der Fachsprache "Spagetticode" weil es nur sehr schwer zu verfolgen ist.

sind weit hergeholt. Wer gegen Goto´s ist, wird immer solche extrem-Beispiele
bringen.

Ich sage: Wer mit Goto's nicht umgehen kann, kann auch ansonsten
nicht "strukturiert" programmieren. Ein klares Goto an geeigneten Stellen
ist mir 100x lieber als eine x-fach verschachtelte Strukturscheisse.
Basic Pur = PureBasic
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag von ts-soft »

Solange das goto innerhalb des Hauptprogrammes, bzw. innerhalb einer Prozedure bleibt, ist es manchmal wirklich effizienter. Sollte aber innerhalb eines Programmes eher die Ausnahme als die Regel sein.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Ypser
XMas-Contest-Gewinner '03
Beiträge: 128
Registriert: 29.08.2004 14:35
Computerausstattung: Win7
Wohnort: Ingelheim
Kontaktdaten:

Beitrag von Ypser »

Und was ist gegen Gosub...Return einzuwenden???

Das ist ein Stück Code das man irgendwo hinpackt (Sinnvollerweise ans Ende des Quellcodes,
vorher ein End zur Sicherheit), und der Aufruf ist wie bei einer Prezedur auch...
Fazit: Gosub ist dein Freund!
Bild
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag von ts-soft »

Gosub : Return wird ja als Unterprogramm benutzt, hierfür ist eine Prozedure besser, wegen lokaler Variablen usw.
Bei der Spieleprogrammierung mag Gosub ja noch manchmal sinnvoll sein, da es etwas schneller als Proceduren ist, aber bei GUI-Anwendungen sollte man wohl eher auf Gosub verzichten.
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Antworten