ProgrammstrukturenVerzweigungen - der einfache Sprung Bedingte Bearbeitung von Befehlen So verschieden die einzelnen Programmiersprachen auch sind, so gibt es doch einige grundlegende Iterationen - Schleifen: StrukturStrukturen, die fast allen gemein sind. Dabei kann man den Befehlsumfang eines Interpreters, Compilers oder Assemblers grob in drei Gruppen einteilen:
Die 'Aktionen' umfassen alle Befehle, die 'etwas bewirken', wie etwa 'PRINT', 'PLOT' oder Wertzuweisungen wie 'LET'. 'Steueranweisungen' sind Befehle, die normalerweise die Programm-Ausführung nicht beeinflussen, wie beispielsweise 'TRON' und 'TROFF' zur Fehlersuche in Einleitung: BASIC Unter dem Begriff 'Grundlagen: ProgrammstrukturenProgrammstrukturen' werden all die Befehle zusammengefasst, die den Ablauf eines Programmes verändern. Ein Programm, das nur aus strukturierenden Befehlen besteht, würde genau nichts bewirken. Die Iterationen - Schleifen: StrukturStrukturen verändern aber das lineare Ablaufen des Programmes und bestimmen so die Reihenfolge, in der die Aktionen abgearbeitet werden. Beispiele sind 'GOTO', 'GOSUB', 'WHILE-WEND' etc. Verzweigungen - der einfache SprungDie einfachste Iterationen - Schleifen: StrukturStruktur überhaupt, die fast jede Sprache bereithält, ist der Sprung. Normalerweise wird der Programmtext Befehl für Befehl abgearbeitet. Dafür benutzt die Befehlshol-Einheit des Mikroprozessors oder des Basic-Interpreters einen Zeiger. Der Zeiger zeigt auf einen Befehl, der Befehl wird eingelesen und der Zeiger weitergestellt. Dann wird der Befehl bearbeitet. Dieser Zyklus läuft, mit geringen Variationen, immer wieder gleich ab:
Beim Sprungbefehl wird der Befehls-Zeiger in der Befehls-Ausführphase auf die angegebene Stelle eingestellt. Er wird mit einer neuen Adresse geladen. In Einleitung: BASIC Einleitung: BASIC
Mit solchen unbedingten Sprüngen (unbedingt = ohne Bedingung) kann man aber nur endlose Schleifen bilden (indem man immer wieder zurückspringt) oder zwei Programmpfade zusammenführen: Endlos-Schleife Zusammenführen von Programmpfaden: --------------- ----------------------------------- 50 PRINT "#"; oder: 50 LET C=100+250 : REM Berechnung 1 60 GOTO 50 60 GOTO 80 70 LET C=125+175 : REM Berechnung 2 75 ' 80 PRINT C : REM Gemeinsame Druckroutine 90 END Man kann eine Verzweigung aber auch an eine Bedingung knuepfen und spricht dann vom bedingten Sprung. Hiermit sind prinzipiell bereits alle Probleme loesbar. Beispielsweise kann man so Schleifen mit Abbruchkriterium herstellen: 100 LET I=1 oder: ZAEHLE: LD Operationen: BD5B / 349A / 349A: FLO SUBA,1 110 PRINT I Z1: PUSH AF 120 LET I=I+1 Maschinencode über HIMEM: CALLCALL PRINT 130 IF I<25 THEN GOTO 110 POP AF 140 STOP INC Operationen: BD5B / 349A / 349A: FLO SUBA CP 25 JR C,Z1 RET Bedingte Bearbeitung von BefehlenDie einfachen Sprungbefehle gehören in der Informatik so mit zum Unbeliebtesten, was man sich denken kann. Mit ihrer Hilfe ist es nämlich möglich, ein Programm völlig unverständlich zu gestalten: Sprung hierhin, dahin, dorthin. Keiner weiß Bescheid. Jede höhere Sprache stellt deshalb Strukturierungsmittel bereit, die sich zwar alle auf bedingte Sprünge zurückführen lassen, bei denen aber Sinn und Zweck der Verzweigungen offensichtlicher wird. Einleitung: BASIC 4 REM HI-LO-Spiel 5 REM ----------- 10 z=Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: INT - Interrupt In HI-LO gilt es eine Zahl 'z' zu erraten. Nach der Eingabe von 'LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION Ist 'LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION In Einleitung: BASIC 4 REM HI-LO-Spiel 5 REM ----------- 10 z=Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: INT - Interrupt Die Einrückungen werden vom Basic-Interpreter natürlich nicht ausgewertet sondern dienen nur dazu, optisch darzustellen, welches THEN und ELSE zu welcher IF-Abfrage gehört. Der Basic-Interpreter benutzt leider einen etwas unglücklichen Algorithmus um festzustellen, welches ELSE zu welchem IF gehört. Und zwar sucht er in der Programmzeile immer nach dem nächsten ELSE. Im folgenden Programm wird der erste ELSE-Pfad abgearbeitet, wenn Test_1 aber auch Test_2 mit Falsch beantwortet wird: 5 REM dieses Vergleichsprogramm funktioniert nicht im Schneider-Basic 6 REM ------------------ 10 INPUT "zwei Zahlen bitte:",Operationen: BD5B / 349A / 349A: FLO SUBa,LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Der zweite ELSE-Pfad kann niemals abgearbeitet werden. Durch das Einrücken ist zwar sinnfällig gemacht, dass, wenn der erste Erklärung der Anschlussbelegung: TestTest (Operationen: BD5B / 349A / 349A: FLO SUBa<=LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Andere Basic-Interpreter oder auch Übergabe von Argumenten und Ergebnissen: PASCAL:PASCAL sind da besser: program vergleich (input,output); var Operationen: BD5B / 349A / 349A: FLO SUBa,LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Übergabe von Argumenten und Ergebnissen: PASCAL:PASCAL macht es sich leicht, indem nach 'then' und 'else' nur ein einziger Befehl folgen darf, der Bedingungs-abhängig abgearbeitet werden soll. Werden mehr benötigt, dann können beliebig viele Einzeloperationen mit 'begin' und 'end' zu einem einzigen Befehl geklammert werden. Die innere Fall-Abfrage im obigen Beispiel 'if Operationen: BD5B / 349A / 349A: FLO SUBa=LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Jede IF-Verzweigung kann nach einem einfachen Schema in bedingte Sprünge zerlegt werden: 4 REM HI-LO-Spiel 5 REM ----------- 10 z=Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: INT - Interrupt Da es in Einleitung: BASIC Indirekter Sprung - Verzweigung via TabelleWenn mehrere Fälle zu unterscheiden sind, empfiehlt sich oft ein indirekter Sprung. Hierbei muss sich aus der Bedingung ein Zeiger berechnen lassen, mit dessen Hilfe aus einer Tabelle das Sprungziel ausgelesen werden kann. In Einleitung: BASIC 10 PRINT " 1 = Laden 2 = Speichern 3 = Überprüfung 4 = Exit" 20 INPUT " bitte wählen Sie.",wahl 30 IF wahl<1 OR wahl>4 THEN GOTO 20 40 ON wahl GOTO 2000,3000,4000,5000 ... Hierbei wird in Zeile 30 zuerst getestet, ob die Eingabe im gültigen Bereich liegt. In Einleitung: BASIC 5 REM Verteiler in einem Textverarbeitungsprogramm 6 REM -------------------------------------------- 10 LET i$ = INKEY$: IF i$="" THEN GOTO 10 20 LET wahl=ASC(i$) 30 IF wahl >= 32 THEN GOTO 5000 : REM kein Controlcode -> Zeichen drucken 40 ' 50 Controlcode behandeln 60 ' 70 ON wahl+1 GOTO 200, 300, 400, 500, 600, 700, 800, 900 80 ON wahl-7 GOTO 1000,1100,1200,1300,1400,1500,1600,1700 90 ON wahl-15 GOTO 1800,1900,2000,2100,2200,2300,2400,2500 95 ON wahl-23 GOTO 2600,2700,2800,2900,3000,3100,3200,3300 ... Bei einer Bereichs-Unterschreitung um Eins macht Einleitung: BASIC ON 0 GOTO 200 [ENTER] Erklärung zu den Bezeichnungen: READY Vor allem aber in Assembler- oder compilierten Programme ist diese sogenannte 'Plausibilitäts-Kontrolle' unerlässlich. In Assembler muss man sich eben um fast alles selbst kümmern. Wenn man nicht kontrolliert, dass der 'wahl'-Zeiger auch wirklich in die Tabelle möglicher Sprung-Adressen zeigt, und nicht darüber oder darunter, landet das Programm im Nirwana, sobald der Anwender ungültige Eingaben macht: ; Verzweigung via Tabelle in Maschinencode: ; ----------------------------------------- MENUE: LD HL,MENTXT ; Menü auf dem Bildschirm ausgeben. Maschinencode über HIMEM: CALLCALL MESSEG ; MEN1: Maschinencode über HIMEM: CALLCALL INKEY ; Warte, dass Anwender eine Taste drückt. ; SUB "0" ; ASCII-Code für "0" bis "9" ; in ein Datentypen: Bytes Iterationen - SchleifenEins der einfachsten Strukturierungsmittel ist die Schleife. Der Befehlssatz der Die ICs im Überblick: Die CPU Z80 Um Schleifen optisch besser erkennbar zu machen, werden die Befehle innerhalb der Schleife oft eingerückt. Aber praktisch keine Programmiersprache wertet dieses Einrücken auch aus. Es dient ausschließlich der besseren Überschaubarkeit. Schleifen dienen dazu, den in ihnen enthaltenen Programmteil mehrfach auszuführen und müssen normalerweise ein Endkriterium haben. Fehlt dies, wird die Schleife unendlich oft durchlaufen. Kommen in einem Programm mehrere Schleifen vor (was ja sehr wahrscheinlich ist), so dürfen sie nicht 'verschränkt' werden: Erlaubt: Erlaubt: Unsinnig: aufeinander folgende ineinander geschachtelte verschränkte Schleifen: Schleifen: Schleifen: prog: befehl prog: befehl prog: befehl befehl start1 -----------+ befehl start1 ----+ befehl | start1 --------+ befehl | befehl | befehl | befehl | start2 -----+ | befehl | befehl | befehl | | start2 -------+ ende1 ----+ befehl | | befehl | | befehl ende2 -----+ | befehl | | start2 ----+ ende1 -----------+ ende1 --------+ | befehl | befehl befehl | ende2 ----+ befehl ende2 -------+ ... ... ??? Das Einleitung: BASIC 100 FOR Operationen: BD5B / 349A / 349A: FLO SUBA=1 TO 10 STEP 1 oder: 200 Operationen: BD5B / 349A / 349A: FLO SUBA=1 110 PRINT Operationen: BD5B / 349A / 349A: FLO SUBA 210 WHILE A>0 120 NEXT Operationen: BD5B / 349A / 349A: FLO SUBA 220 PRINT Operationen: BD5B / 349A / 349A: FLO SUBA 130 END 230 Operationen: BD5B / 349A / 349A: FLO SUBA=Operationen: BD5B / 349A / 349A: FLO SUBA/2 240 WEND 250 END Die FOR/NEXT-Schleife bietet dabei den zusätzlichen Service, eine so genannte LAUFVARIABLE nach jedem Schleifendurchgang automatisch weiterzustellen. Im Schleifenkopf muss dabei der Laufbereich (von...bis) und die Schrittweite angegeben werden. Das Schleifenende wird durch den Befehl NEXT festgelegt. FOR/NEXT-Schleifen weisen leider von Einleitung: BASIC Laufindex10 FOR i=1 TO 10:PRINT i:NEXT i 20 PRINT i Welchen Wert hat im obigen Programm die Unterprogramme: VariablenVariable Operationen: BD5B / 349A / 349A: FLO SUBA nach dem letzten Schleifendurchlauf, 10? Falsch. Wenn Sie das Programm abtippen, können Sie sich davon überzeugen, dass Operationen: BD5B / 349A / 349A: FLO SUBA zum Schluss den Wert 11 enthält. Bei manchen anderen Interpretern ist aber auch der Wert 10 möglich. Das hängt davon ab, wie der Interpreter das Schleifenende testet: Zuerst die Laufvariable weiterstellen und dann auf Überschreitung der Grenze Erklärung der Anschlussbelegung: Testtesten (wie Locomotive Einleitung: BASIC StrukturEin weiterer Unterschied ergibt sich in der Art, wie der Interpreter den Zusammenhalt zwischen dem Schleifenkopf (FOR-Statement), dem Schleifenende (NEXT) und der Laufvariablen wahrt. Locomotive Einleitung: BASIC 80 INPUT "Startwert";S 90 INPUT " Endwert";LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION Beim Spectrum werden die Schleifenparameter nämlich an die Laufvariable gebunden. Hier ist die NEXT-Adresse nicht angegeben, nur die Adresse des FOR-Statements. Dadurch verzweigt dessen Einleitung: BASIC Das folgende Programm kann, je nach Basic-Interpreter, recht unterschiedliche Wirkungen entfalten. Die Wirkung wird aber nur in den seltensten Fällen die sein, die man von ihm erwartet: 10 FOR Operationen: BD5B / 349A / 349A: FLO SUBA=10 TO 100 STEP 10 20 FOR Operationen: BD5B / 349A / 349A: FLO SUBA=Operationen: BD5B / 349A / 349A: FLO SUBA TO Operationen: BD5B / 349A / 349A: FLO SUBA+9 STEP 1 30 PRINT Operationen: BD5B / 349A / 349A: FLO SUBA 40 NEXT Operationen: BD5B / 349A / 349A: FLO SUBA 50 NEXT Operationen: BD5B / 349A / 349A: FLO SUBA abweisende SchleifenAlle Schleifen lassen sich in zwei unterschiedliche Kategorien einordnen: Die Iterationen - Schleifen: abweisende Schleifenabweisenden Schleifen, bei denen der Schleifeninhalt kein einziges Mal abgearbeitet wird, wenn das Abbruchkriterium bereits vor dem ersten Durchlauf erreicht ist, und die nicht Iterationen - Schleifen: abweisende Schleifenabweisenden Schleifen, deren Inhalt mindestens einmal durchlaufen wird, bevor eine Endabfrage über weitere Wiederholungen entscheidet. Für diese beiden Kategorien gibt es auch die nicht ganz ernst gemeinten Bezeichnungen 'Beamtenschleife' (erst nachfragen, ob man wirklich was tun muss) und 'Funktionaersschleife' (Hauptsache, es wird was getan. Nachfragen, ob es sinnvoll war, kann man ja immer noch.). Auch hierin können sich die FOR/NEXT-Schleifen von Basic-Interpreter zu Basic-Interpreter unterscheiden. Das Einleitung: BASIC 100 FOR Operationen: BD5B / 349A / 349A: FLO SUBA=100 TO 99 STEP +1 110 PRINT Operationen: BD5B / 349A / 349A: FLO SUBA 120 NEXT Operationen: BD5B / 349A / 349A: FLO SUBA RUN [ENTER] Break in 130 Erklärung zu den Bezeichnungen: READY In diesem Fall wird die Schleife kein einziges Mal durchlaufen, und auch der Schleifenindex nicht vor der Endabfrage erhöht. Operationen: BD5B / 349A / 349A: FLO SUBA enthält nach 'Durchlauf' der Schleife den Wert 100. Für die WHILE/WEND-Schleife gibt es da schon verlässlichere Kriterien. Auch sie ist eine Iterationen - Schleifen: abweisende Schleifenabweisende Schleife. Der Schleifeninhalt wird so lange durchlaufen, wie die im Schleifenkopf angegebene Bedingung erfüllt ist (While = deutsch: während). Während unser Einleitung: BASIC nicht abweisend: abweisend: START: LD LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL Hier ist der Unterschied besonders krass. Das Ergebnis beider Schleifen ist das selbe, solange der Startwert für LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL |