Kontrollstruktur

Kontrollstrukturen sind in der Informatik die Vorgabe, in welcher Reihenfolge die Handlungsschritte eines Algorithmus abgearbeitet werden. In imperativen Programmiersprachen werden sie durch Kontrollanweisungen (Steuerkonstrukte) implementiert. Mit Kontrollstrukturen können Programme auf verschiedene Zustände reagieren, indem Programmteile nur bedingt (bedingte Anweisung) oder wiederholt (Schleife) ausgeführt werden.[1]

Struktogramm

Geschichtliche Entwicklung

Kontrollstrukturen helfen Sprünge mit Goto zu vermeiden, da fast jede Programmieraufgabe mit Hilfe von Bedingungs- und Wiederholungsstrukturen gelöst werden kann. Programmierung ohne Goto unter Verwendung einer modernen Sprache ist heute der Normalfall. Die Programmiersprache Java unterstützt z. B. keine Goto-Anweisung mehr.

Heinz Rutishauser entwickelte von 1949 bis 1951 die einfache algebraische Programmiersprache „Superplan“. Rutishauser kannte Konrad Zuses Arbeit über Programmiersprachen, d. h. Zuses Plankalkül und wählte den Namen in Anlehnung an Zuses Bezeichnung „Rechenplan“ für ein einzelnes Programm. Rutishausers einfache Sprache hatte nur eine Kontrollstruktur: die Für-Schleife.

Für i=2(1)n:  + 3 = 

In den frühen 1960er Jahren waren Flussdiagramme und Sprunganweisungen in Programmen üblich, was größere Programme nahezu unwartbar machte, da sie schnell unüberschaubar wurden. 1968 sprach sich Edsger W. Dijkstra in seinem Aufsatz Go To Statement Considered Harmful (der Titel geht allerdings auf Niklaus Wirth zurück), für eine Abschaffung des GOTO-Befehls in allen höheren Programmiersprachen aus.[2]

Im Mai 1966 publizierten Böhm und Jacopini in der Zeitschrift Communications of the ACM einen Artikel, in dem sie zeigten, dass jedes Programm, das Goto-Anweisungen enthält, in ein Goto-freies Programm umgeschrieben werden kann, das nur mit Verzweigung (IF THEN ELSE) und einer Schleife (WHILE Bedingung DO Anweisung) arbeitet, gegebenenfalls unter Zuhilfenahme von etwas Code-Doppelung und der Einführung von booleschen Variablen (true/false).

Anfang der 1970er Jahre wurde damit begonnen, diesen Ansatz zur Selbstbeschränkung auf wenige, typische Elemente umzusetzen. Wegweisend war die Arbeit von Niklaus Wirth mit seiner Programmiersprache Pascal.

Kontrollstrukturen

Konventionelle Programmiermodelle und -sprachen konstruieren Algorithmen mit Hilfe folgender Kontrollstrukturen:[3]

Das Programmierparadigma der Strukturierten Programmierung beinhaltet neben dem Paradigma der prozeduralen Programmierung die Beschränkung auf lediglich drei Kontrollstrukturen Sequenz, Verzweigung und Schleifen.

Die strukturierte Programmierung verhindert das Entstehen von Quelltext, bei dem auf unübersichtliche Weise hin- und hergesprungen wird, dem sogenannten Spaghetti-Code. Die folgenden Regeln dienen der Nutzung von Kontrollstrukturen:

  • Anweisungen, die wiederholt werden, sind durch Schlüsselwörter klar gekennzeichnet.
  • Bedingungen und Wiederholungen sind sofort erkennbar.
  • Sprunganweisungen und Sprungmarken werden vermieden.

Kontrollanweisungen

Kontrollanweisungen (auch Kontrollflussanweisungen, Steuerkonstrukte) sind Anweisungen, die eine Kontrollstruktur implementieren.[4]

Blockstrukturanweisung (Sequenz)

Blockstrukturanweisungen kennzeichnen Beginn und Ende einer Blockstruktur (Sequenz) und strukturieren damit Programmteile. In Pascal oder Visual Basic werden sie mit begin ... end gekennzeichnet, in einigen Programmiersprachen werden sie durch Klammernpaare gekennzeichnet. Im Quelltext werden die Anweisungen einer Sequenz einheitlich eingerückt, um die Blockstruktur besser sichtbar zu machen. In Python wird auf Blockstrukturanweisungen verzichtet; die Zugehörigkeit von Anweisungen zu Blöcken wird hier allein durch Einrückung gekennzeichnet.

Bedingte Anweisung

Eine Bedingte Anweisung ist eine Anweisung, die nur bei der Erfüllung einer bestimmten Bedingung ausgeführt wird. Durch sie kann ein Programm auf unterschiedliche Zustände und Benutzereingaben reagieren. Eine Bedingte Anweisung besteht aus einer Bedingungsprüfung und der eigentlichen Anweisung IF <Bedingung> THEN <Anweisung>. Nur dann wenn eine Bedingung erfüllt ist, wird die Anweisung ausgeführt. Koppelt man eine Bedingungsprüfung und einen Sprungbefehl if bedingung then methode(), so erhält man eine Verzweigung, bei der eine Methode nur bei positiver Auswertung des Bedingungsausdrucks (TRUE) ausgeführt wird. Durch die Prüfung des Zustands einer Zählervariable können auch Schleifen implementiert werden.

Verzweigung (Alternative)

Für eine alternative Verzweigung bieten viele Programmiersprachen das IF-THEN-ELSE Konstrukt in der Form IF <Bedingung> THEN <Anweisung1> ELSE <Anweisung2>. Bei positiver Bedingung wird Anweisung1 ausgeführt. In allen anderen Fällen (ELSE) wird Anweisung2 ausgeführt. So kann ein Programm beispielsweise abhängig von einer Eingabe zwei unterschiedliche Methoden aufrufen. Viele Sprachen erlauben weitere Prüfungen und damit verbundene alternative Verzweigungen in der Form IF [[ELIF],..] ELSE oder ein Konstrukt aus Case- und Switch-Anweisungen.

Beispiel in Python (Die Funktion vorzeichen ermittelt das Vorzeichen des Ausdrucks i):

def vorzeichen(i):
    if i>0:                   #Bedingung
        return "positiv +"    #bedingte Anweisung1
    elif i<0:                 #weitere Bedingung
        return "negativ -"    #bedingte Anweisung2
    else                      #anderenfalls
        return "neutral 0"    #bedingte Anweisung3

Schleife (Wiederholung, Iterationsanweisung)

Schleifen dienen dazu, Anweisungs-Blöcke wiederholt auszuführen. Es gibt viele Erscheinungsformen von Schleifen:

Beispiel in Python (Für die Zahlen −5, 2, 0 und 1000 wird nacheinander das jeweilige Vorzeichen ausgegeben):

for i in (-5,2,0,1000):
    print (vorzeichen(i))

In den Schleifen gibt es je nach Programmiersprache Anweisungen

  • zum vorzeitigen Verlassen von Schleifen (break)
  • zur Fortsetzung mit der nächsten Iteration (continue)
  • zur Wiederholung der aktuellen Iteration (retry) oder
  • zum erneuten Starten der Schleife (redo Ruby (Programmiersprache))

List-Comprehensions erzeugen Mengen mit Schleifen- und Bedingungskonstrukten:

m = [x**2 for x in range(12) if x%2 == 0] #ergibt die Quadratzahlen aus geraden x

Sprunganweisung

Die Anweisungen in einer Sequenz werden sequentiell, also nacheinander, abgearbeitet. Bei einem Sprung wird eine durch Adresse oder durch Label festgelegte Sprungmarke angesprungen und der Programmverlauf dort fortgesetzt.

Bedingte Sprunganweisungen (im Maschinencode) sind meist die Grundlage für bedingte Ausführung sowie Schleifen (in der Hochsprache); unbedingte Sprunganweisungen (im Maschinencode) sind meist die Grundlage für Funktionsaufrufe oder Endlosschleifen.

Beim Aufruf eines Unterprogramms wird zunächst abgespeichert, wohin nach dem Ende des Unterprogramms zurückzukehren ist. Anschließend müssen ggf. Aufrufparameter an eine vereinbarte Stelle gelegt werden; erst danach kann das Unterprogramm angesprungen werden.

Die Verwendung von Sprunganweisungen in Hochsprachen ist umstritten, da sie schnell zu Spaghetticode führen kann. Sprunganweisungen lassen sich (in Hochsprachen) durch andere Kontrollstrukturen ersetzen.

Rücksprung

Wird bei einem Sprungbefehl ein Unterprogramm oder eine andere Methode aufgerufen, so kann diese mittels return wieder verlassen und das Programm direkt nach dem Absprungpunkt fortgesetzt werden. Dabei können sogenannte Rückgabewerte übergeben werden.

Ereignis- und Ausnahmebehandlung

Mit der Ausnahmebehandlung kann ein Programm die Kontrolle bei bestimmten Programmzuständen – meistens Fehlerzuständen – an andere Programmebenen zur Weiterbehandlung weiterzureichen. Ausnahmen oder Exceptions haben in weiten Teilen die Behandlung von Fehlern mittels Fehlercodes oder Sprunganweisungen abgelöst.

Grafische Darstellung

UML-Diagramme

Kontrollstrukturen können mittels Diagrammen visualisiert und modelliert werden.

Flussdiagramm

Aktuell gebräuchlich sind hierfür die UML-Sequenzdiagramme als Weiterentwicklung der Struktogrammen (Nassi-Shneiderman-Diagrammen) und UML-Aktivitätsdiagramme als Weiterentwicklung der Flussdiagramme.

In Struktogrammen wird die Kontrollstruktur durch entsprechende Symbole für Verzweigung und Schleife deutlicher. Struktogramme lassen sich dann in einen strukturierten Programmcode umsetzen.

Weitere Kategorisierungen

Die Arten von Kontrollflussanweisungen, die von verschiedenen Sprachen unterstützt werden, variieren, können nach ihrer Wirkung kategorisiert werden:

  • Kennzeichnung des Beginns und Endes einer Blockstruktur (Blockstrukturanweisung)
  • Fortsetzung bei einer anderen Anweisung (Sprunganweisungen)
  • Beenden des Programms oder Prozedur, Methode oder Funktion, Verhindern einer weiteren Ausführung (Rücksprung bedingungsloser Stopp)
  • Ausführen einer (Reihe von) Anweisungen nur, wenn eine Bedingung erfüllt ist (bedingte Anweisung oder Verzweigung)
  • Ausführen einer Reihe von Anweisungen in einer Schleife nicht oder mehrmals, bis oder solange wie eine Bedingung erfüllt ist (Iterationsanweisung)
  • Ausführen einer Reihe entfernter Anweisungen, nach Eintritt eines Ereignisses (Ereignis- und Ausnahmebehandlung)

Manche Kontrollanweisungen, wie zum Beispiel der unbedingte Sprung, lassen sich nicht eindeutig einer Kontrollstruktur zuordnen oder können für mehrere Kontrollstrukturen notwendig sein.

Viele Programmiersprachen wie zum Beispiel imperative Sprachen enthalten Kontrollanweisungen.

Einzelnachweise

  1. Reisig, Wolfgang, 1950-, Freytag, Johann Christoph, 1954-: Informatik : aktuelle Themen im historischen Kontext. Springer, Berlin 2006, ISBN 978-3-540-32743-1, S. 278.
  2. Edsger W. Dijkstra: Letters to the editor: Go To Statement Considered Harmful. In: Communications of the ACM. Band 11, Nr. 3, März 1968, S. 147–148, doi:10.1145/362929.362947.
  3. Reisig, Wolfgang, 1950-, Freytag, Johann Christoph, 1954-: Informatik : aktuelle Themen im historischen Kontext. Springer, Berlin 2006, ISBN 978-3-540-32743-1, S. 301.
  4. Steyer, Ralph,: Programmierung in Python : Ein kompakter Einstieg für die Praxis. Wiesbaden, ISBN 978-3-658-20705-2, S. 87.