Die ersten Artikel dieser Reihe haben sich sich mit den allgemeinen Konzepten von Funktionen und Variablen beschäftigt. Wir haben gesehen, wie man neue Funktionen definiert, Variablen deklariert, ihnen Werte zuweist, sie an Funktionen übergibt und ihren Wert als Ergebnis einer Funktion zurückgibt. Heute wollen wir uns mit einem weiteren wichtigen Konzept beschäftigen: den Kontrollstrukturen.
Funktionsaufrufe sind eine Form, den Kontrollfluss im Programm, den Programmablauf, zu beeinflussen; hierbei wird von einer Stelle innerhalb einer Funktion zu einer anderen Funktion gesprungen, diese ausgeführt, und dann zum ursprünglichen Ort der Ausführung zurückgesprungen und dort fortgefahren. Funktionsaufrufe sind in ihrer ursprünglichen Form streng linear und eindeutig: beim Funktionsaufruf ist bekannt, an welche Stelle im Programm gesprungen werden und welcher Code dort ausgeführt werden soll (das gilt nicht für die objektorientierte Programmierung – aber dazu in einem späteren Artikel mehr).
Für komplexere Programme werden aber auch andere Formen der Beeinflussung des Kontrollflusses benötigt; hierfür werden die sogenannten Kontrollstrukturen verwendet. Besonders häufig benötigte Kontrollstrukturen sind Verzweigungen und Wiederholungen. Bei einer Verzweigung wird abhängig von einer Bedingung eine von 2 Anweisungen oder Anweisungsfolgen ausgeführt, wohingegen bei einer Wiederholung eine Anweisungsfolge so lange wiederholt wird, bis eine bestimmte Bedingung nicht mehr gilt. Aber im Detail.
Verzweigungen
Eine Verzweigung teilt den Programmfluss in 2 mögliche Pfade auf. Je nach dem, ob eine angegebene Bedingung erfüllt ist, wird entweder eine oder der andere Anweisungspfad ausgeführt. In C++ lassen sich Verzweigungen in der folgenden Art (an einem Beispiel) definieren:
int abs( int n ) { if ( n > 0 ) return n; else return -n; }
Gut, was sehen wir hier? Als erstes einmal die Definition einer Funktion abs
, welche eine ganze Zahl als Parameter verlangt und eine ebensolche als Rückgabewert zurückliefert. Die Verzweigung innerhalb der Funktion besagt nun folgendes: Wenn der Parameter n größer als 0 ist, dann soll er direkt zurückgegeben werden; ansonsten (sprich, wenn er kleiner oder gleich 0 ist) soll er negiert zurückgegeben werden – die klassische Definition des Absolutwertes. Anders gesagt: ist die Bedingung der Verzweigung erfüllt, wird die Anweisung hinter dem if
ausgeführt, ansonsten die hinter dem else
(englisch für “sonst”).
In den beiden Zweigen können natürlich nicht nur einzelne Anweisungen, sondern ganze Anweisungsblöcke stehen, die, ebenso wie bei einer Funktion, in geschweifte Klammern eingefasst sind. In diesen Anweisungsblöcken können auch neue Variablen definiert werden, die dann nur innerhalb dieses Blocks gültig sind; der folgende Code würde zu einem Fehler führen, da die Variable m
hinter der Anweisung nicht bekannt ist:
int abs( int n ) { if ( n > 0 ) { int m; m = n; } else { int m; m = -n; } return m; }
Gültig wäre dagegen der folgende Code (an welchem man auch gleich sieht, dass man die geschweiften Klammern bei Anweisungsfolgen mit nur einer Anweisung auch weglassen kann; das ganze auch gemischt innerhalb einer Verzweigung):
int abs( int n ) { int m; if ( n > 0 ) { m = n; } else m = -n; return m; }
Die Angabe des else
-Zweiges ist übrigens nicht obligatorisch; wird er weggelassen, wird einfach kein Code in der Verzweigung ausgeführt, wenn die Bedingung nicht zutrifft (man spricht dann lediglich von einer bedingten Anweisung), etwa wie in dem folgenden Beispiel; das ist wieder die Funktion zur Berechnung des Absolutwertes, nur etwas anders formuliert:
int abs( int n ) { if ( n < 0 ) n = -n; return n; }
Lässt sich eine Bedingung nicht in lediglich 2 Zweige aufbrechen, so können auch mehrere Verzweigungen hintereinander geschaltet werden, etwa so (zur Definition einer mathematischen Funktion; deren Nutzen sei einmal egal):
int f( int m, int n ) { if ( n < 0 ) return -n; else if ( n > 0 ) return n; else return m; }
Wiederholungen
Die Kontrollstruktur der Wiederholung wird verwendet, wenn eine Anweisung oder Anweisungsfolge mehrere Male ausgeführt werden soll. Es lassen sich verschiedenste Formen der Wiederholung unterscheiden, wobei die while-Schleife die einfachste Form ist; in ihr wird eine Anweisung einfach so lange wiederholt, bis eine bestimmte Bedingung nicht mehr gilt. Das sieht in C++ dann zum Beispiel so aus:
int sum( int n ) { int s = 0; while ( n > 0 ) { s = s + n; n = n - 1; } return s; }
Die Funktion sum
macht hierbei nichts anderes als die Summe der Zahlen von 1
bis n
(oder, um den Programmablauf besser sprachlich abzubilden: von n
bis 1
) zu berechnen. Die Schleife wird dabei so oft durchlaufen, wie die Variable n
einen Wert größer als 0 besitzt; in jedem Durchlauf wird das aktuelle n
auf den Wert von s
addiert und n
um eins verringert. Denkbar einfach also.
Eine zweite, häufig anzutreffende Wiederholung in C++-Code ist die Zählschleife oder for-Schleife. Diese erlaubt (unter anderem), eine Schleife eine festgelegte Anzahl von Malen zu durchlaufen. Zu diesem Zweck wird eine Laufvariable definiert und für diese der Startwert, der Endwert (in Form einer Bedingung) und die Schrittweite (in Form eines Ausdruckes) angegeben. Die Summen-Funktion lässt sich damit auch folgendermaßen schreiben:
int sum( int n ) { int s = 0; for ( int i = 1; i <= n; i = i + 1 ) s = s + i; return s; }
In Worten besagt die Schleife folgendes: deklariere eine Variable i
(die übrigens nur innerhalb der Schleife gültig ist) und weise ihr den Wert 1 zu; solange i
kleiner oder gleich n
ist (<=
ist der C++-Operator für ≤), führe die Anweisungen in der Schleife aus und erhöhe am Ende jedes Durchlaufes i
um 1.
Neben der while- und for-Schleife gibt es noch die do-while-Schleife, die ähnlich wie die while-Schleife funktioniert, die Bedingung aber am Ende eines Schleifendurchlaufes und nicht am Anfang prüft; einziger Unterschied zur while-Schleife ist damit, dass die Anweisungen in der Schleife wenigstens ein mal auf jeden Fall ausgeführt werden - unabhängig von der Bedingung. Geschrieben wird sie so:
int sum( int n ) { int s = 0; do { s = s + n; n = n - 1; } while ( n > 0 ); return s; }
Beim Verwenden von Schleifen ist auf eine Tücke zu achten: wird nicht sichergestellt, dass die Bedingung einer Schleife irgendwann nicht mehr gilt, kann man in eine sogenannte unendliche Schleife geraten, also eine Schleife, die niemals endet und immer wieder durchlaufen wird - was dazu führt, dass ein Programm nicht mehr reagiert und/oder abstürzt.
Zwei Konzepte sind bei der Verwendung von Schleifen noch interessant: das vorzeitige Abbrechen einer Schleife und das vorzeitige Abbrechen des aktuellen Schleifendurchlaufes.
Der vorzeitige Abbruch der gesamten Schleife erfolgt mit Hilfe des Schlüsselwortes break
; wird es im Schleifenkörper angetroffen, so wird die Schleife sofort abgebrochen. Hier ein kleines Beispiel einer Summen-Schleife, die abbricht, sobald die berechnete Summe einen Wert größer als 1000 erreicht:
int sum1000( int n ) { int s = 0; while ( n > 0 ) { s = s + n; n = n - 1; if ( s > 1000 ) break; } return s; }
Mit Hilfe des Schlüsselwortes continue
kann dagegen lediglich der aktuelle Schleifendurchlauf abgebrochen werden; wird es angetroffen, beginnt die Schleife sofort mit dem nächsten Durchlauf. Das folgende Beispiel zeigt eine Schleife, welche lediglich gerade Zahlen aufsummiert (der %
-Operator ist der Restwert-Operator, der den Rest einer ganzzahligen Division zurückgibt):
int sum1000( int n ) { int s = 0; while ( n > 0 ) { if ( n % 2 != 0 ) { n = n - 1; continue; } s = s + n; n = n - 1; if ( s > 1000 ) break; } return s; }
So viel erst einmal zum Thema Verzweigungen und Schleifen. Der nächste Artikel wird wieder etwas technischer, da wir uns da etwas mit den Geheimnissen des Stacks beschäftigen werden.
Kommentare (23)