Programmiertechnik Kontrollstrukturen Prof. Dr. Oliver Haase Oliver Haase Hochschule Konstanz 1 Was sind Kontrollstrukturen? Kontrollstrukturen erlauben es, ein Programm nicht nur Zeile für Zeile auszuführen, sondern in Abhängigkeit bestimmter Bedingungen bestimmte Programmteile auszuführen. Siehe z.B. folgende (Kontroll-)Flussdiagramme: Anweisung Bedingung? Entscheidung false Anweisung Bedingung? true Schleife false true Anweisung Anweisung Anweisung Anweisung Anweisung Anweisung Anweisung Anweisung Oliver Haase Hochschule Konstanz 2 Überblick Kontrollstrukturen steuern den Ablauf eines Programms. Kontrollstrukturen sind spezielle Arten von Anweisungen. In Java (wie in den meisten Programmiersprachen) gibt es verschiedene Arten von Anweisungen: Deklarationsanweisungen Zuweisungsanweisungen Methodenaufruf Blöcke If-Anweisungen Switch-Anweisungen Schleifen Sprünge Oliver Haase hier behandelt Hochschule Konstanz 3 Blöcke Ein Block ist eine mit { und } geklammerte Folge von Anweisungen. Ein Block kann überall dort wo eine Anweisung erlaubt/erfordert ist, als eine einzige Anweisung eingesetzt werden. Innerhalb eines Blocks können auch Variablen deklariert werden, deren Gültigkeit auf den Block beschränkt ist. int i = 1; { } { } Oliver Haase int j = 2; System.out.println(i +j); Blöcke int j = 3; System.out.println(i +j); Hochschule Konstanz 4 If-Anweisung Die If-Anweisung ist die fundamentalste aller Kontrollanweisungen Syntaxregel if (<Bedingung>) <Anweisung> else <Anweisung> <Bedingung> ist ein Ausdruck, der ein Ergebnis vom Typ boolean liefert, z.B. boolsche Variable, logische Operation, oder Vergleich. Auswertung: <Bedingung> falls <Bedingung> true liefert, dann unmittelbar folgende Anweisung falls <Bedingung> false liefert, dann Anweisung unmittelbar nach else. Oliver Haase Hochschule Konstanz 5 If-Anweisung Beispiel Betragsberechnung: double x, y; java.util.Scanner scanner = new java.util.Scanner(System.in); System.out.print("x:"); x = scanner.nextDouble(); if (x < 0.0) y = -x; else y = x; System.out.println("Der Betrag von " + x + " ist " + y); Merke: Nur die Anweisung unmittelbar nach else gehört zum 'Sonst'-Fall, die Bildschirmausgabe nicht mehr! Oliver Haase Hochschule Konstanz 6 If-Anweisung Der else-Teil kann komplett fehlen: Syntaxregel if (<Bedingung>) <Anweisung> Beispiel Betragsberechnung 2: double x, y; java.util.Scanner scanner = new java.util.Scanner(System.in); System.out.print("x:"); x = scanner.nextDouble(); y = x; if (x < 0.0) y = -x; System.out.println("Der Betrag von " + x + " ist " + y); Oliver Haase Hochschule Konstanz 7 If-Anweisung Die Anweisungsteile können selbst Blöcke sein. Damit ergibt sich die Form: Syntaxregel if (<Bedingung>) { <Anweisung> … <Anweisung> } else { <Anweisung> … <Anweisung> } Oliver Haase Hochschule Konstanz 8 If-Anweisung Es ist guter Stil, selbst einzelne Anweisungen zu klammern, um Fehler zu vermeiden (defensives Programmieren). Siehe folgendes Beispiel. Was ist hier falsch? int x = scanner.nextInteger(); if (x == 0) { System.out.println("x ist gleich 0"); } else System.out.println("x ungleich 0, wir dividieren"); System.out.println("1/x liefert " + 1 / x); Oliver Haase Hochschule Konstanz 9 Switch-Anweisung Ebenso wie die If-Anweisung eine Entscheidungsanweisung. Erlaubt, in verschiedene Alternativen zu verzweigen Syntaxregel switch (<Ausdruck>) { case <Konstante>: <Anweisungsfolge> break; … case <Konstante>: <Anweisungsfolge> break; default: <Anweisungsfolge> } Oliver Haase Hochschule Konstanz 10 Switch-Anweisung <Ausdruck> muss byte, short, int, oder char liefern. Wenn Auswertung von <Ausdruck> den Wert <Konstante> liefert, wird die entsprechende <Anweisungsfolge> ausgeführt. Beachte: <Anweisungsfolge>, im Unterschied zu einem Block, muss nicht geklammert werden! Wenn Wert von Ausdruck nicht gefunden wird, wird <Anweisungsfolge> hinter default-Marke ausgeführt. Die default-Marke kann fehlen, dann wird im obigen Fall gar nichts ausgeführt. Oliver Haase Hochschule Konstanz 11 Switch-Anweisung Beispiel: char result; // result wird ein Character zugewiesen switch (result) { case '+': System.out.println("gutes Ergebnis"); break; case '-': System.out.println("schlechtes Ergebnis"); break; case 'o': System.out.println("So-la-la-Ergebnis"); break; default: System.out.println("unbekanntes Ergebnis"); } Oliver Haase Hochschule Konstanz 12 Switch-Anweisung Wenn break-Anweisung fehlt, wird die Ausführung fortgesetzt bis ein break erreicht wird engl: Fall-Through. Praktisch, wenn mehrere Werte denselben Effekt haben sollen: char result; // result wird ein Character zugewiesen switch (result) { case 'o': case '+': System.out.println("bestanden"); break; case '-': System.out.println("durchgefallen"); break; default: System.out.println("unbekanntes Ergebnis"); } Oliver Haase Hochschule Konstanz 13 Switch-Anweisung Ein vergessenes break ist aber auch eine häufige Fehlerursache: int a, b; a = scanner.nextInteger(); switch (a) { case 1: b = 10; case 2: case 3: b = 20; break; default: b = 40; } Welchen Wert erhält b für verschiedene Werte von a? Oliver Haase Hochschule Konstanz 14 Schleifen Schleifen erlauben es, eine Anweisung (einen Block von Anweisungen) mehrmals zu durchlaufen. Java kennt 3 Arten von Schleifen: For-Schleifen While-Schleifen Do-Schleifen Allen Schleifenarten ist gemeinsam, dass sie eine Abbruchbedingung enthalten. Oliver Haase Hochschule Konstanz 15 For-Schleife Syntaxregel for (<Initialisierung>; <Bedingung>; <Update>) <Anweisung> Auswertung: 1. Mit Hilfe der <Initialisierung> wird eine Schleifenvariable deklariert und initialisiert, z.B. int i = 0 2. Die <Bedingung>, die üblicherweise die vorher initialisierte Variable enthält, wird ausgewertet. Beispiel: i < 10 3. Falls Schritt 2 false ergibt, wird die Schleife beendet 4. Falls Schritt 2 true ergibt, wird a) b) c) <Anweisung> ausgeführt <Update> ausgeführt, z.B. i++ mit Schritt 2 fortgefahren Oliver Haase Hochschule Konstanz 16 For-Schleife Auswertung als Kontrollflussdiagramm: Initialisierung Bedingung? false true Anweisung Update Oliver Haase Hochschule Konstanz 17 For-Schleife Beispiel: for ( int i = 0; i < 10; i++ ) System.out.println("i: " + i); <Anweisung> -- der Schleifenrumpf – kann ein geklammerter Block von Anweisungen sein. Ebenso wie bei der If-Anweisung ist es guter Stil, den Schleifenrumpf immer zu klammern. (Warum?) Oliver Haase Hochschule Konstanz 18 For-Schleife Jeder Bestandteil des Schleifenkopfs (Initialisierung, Bedingung, Update) kann fehlen keine Bedingung → entspricht true Beispiel int i = 0; for ( ; i < 10; ) { System.out.println("i: " + (i++)); } Oliver Haase Hochschule Konstanz 19 For-Schleife Der geklammerte Anweisungsblock im Schleifenrumpf kann selbst wieder eine Schleife sein: for ( int i = 0; i < 10; i++ ) { for ( int j = 0; j < i; j++ ) { System.out.println("(" + i + ", " + j + ")"); } } Oliver Haase Hochschule Konstanz 20 While-Schleife Die While-Schleife ist allgemeiner (flexibler) als die For-Schleife. Syntaxregel while (<Bedingung>) <Anweisung> Auswertung: 1. <Bedingung> wird ausgewertet 2. Falls Auswertung false ergibt, wird die Schleife beendet 3. Falls Auswertung true ergibt, wird a) b) Oliver Haase <Anweisung> ausgeführt mit Schritt 1 fortgefahren Hochschule Konstanz 21 While-Schleife Auswertung als Kontrollflussdiagramm: Bedingung? false true Anweisung Oliver Haase Hochschule Konstanz 22 While-Schleife Natürlich kann der Schleifenrumpf ein geklammerter Block von Anweisungen sein. Bei der While-Schleife muss der Programmierer die Schleifenvariable ggf. selbst deklarieren, initialisieren und im Schleifenrumpf aktualisieren. Beispiel: int i = 0; while ( i < 10 ) { System.out.println("i: " + i); i++; } Auch hier ist es guter Stil, den Schleifenrumpf immer zu klammern! Oliver Haase Hochschule Konstanz 23 While-Schleife Jede For-Schleife kann durch eine While-Schleife ersetzt werden. for (<Initialisierung>; <Bedingung>; <Update>) <Anweisung> <Initialisierung>; while (<Bedingung>) { <Anweisung>; <Update>; } Oliver Haase Hochschule Konstanz 24 Do-Schleife Ähnlich wie While-Schleife, nur dass Bedingung erst nach Ausführung des Rumpfs getestet wird Schleife wird mindestens einmal durchlaufen. Syntaxregel do <Anweisung> while (<Bedingung>); Auswertung: 1. 2. 3. 4. <Anweisung> wird ausgeführt <Bedingung> wird ausgewertet Falls Auswertung false ergibt, wird die Schleife beendet Falls Auswertung true ergibt, wird mit Schritt 1 fortgefahren Oliver Haase Hochschule Konstanz 25 Do-Schleife Auswertung als Kontrollflussdiagramm: Anweisung Bedingung? false true Oliver Haase Hochschule Konstanz 26 Do-Schleife Natürlich kann der Schleifenrumpf ein geklammerter Block von Anweisungen sein. Genauso wie bei der While-Schleife muss der Programmierer die Schleifenvariable selbst deklarieren, initialisieren und im Schleifenrumpf aktualisieren. Beispiel: int i = 0; do { System.out.println("i: " + i); i++; } while ( i < 10 ); Auch hier ist es guter Stil, den Schleifenrumpf immer zu klammern! Oliver Haase Hochschule Konstanz 27 Do-Schleife Jede Do-Schleife kann durch eine While-Schleife ersetzt werden. do <Anweisung> while (<Bedingung>); <Anweisung>; while (<Bedingung>) <Anweisung>; Oliver Haase Hochschule Konstanz 28 Endlosschleife Wenn die Schleifenbedingung niemals false ergibt, liegt eine Endlosschleife vor. Endlosschleifen können absichtlich, aber auch versehentlich programmiert werden. Beispiel für absichtliche Endllosschleife: while (true) { System.out.println("Nochmal!"); } Oliver Haase Hochschule Konstanz 29 Endlosschleife Unabsichtliche Endlosschleifen resultieren meist daraus, dass vergessen wird die Schleifenvariable im Schleifenrumpf zu verändern. Beispiel für unabsichtliche Endllosschleife: int i = 0; while (i < 10) { System.out.println("i: " + i); } Oliver Haase Hochschule Konstanz 30 Sprünge In Java gibt es keine goto-Anweisung! Es gibt nur zwei eingeschränktere Sprungvarianten, nämlich: break um eine Schleife (oder eine Switch-Anweisung) zu verlassen continue um zur Schleifenbedingung zu springen Oliver Haase Hochschule Konstanz 31 Break Bereits zusammen mit Switch-Anweisung kennengelernt Springt aus innerster Schleife Wird meistens verwendet, um Schleife vorzeitig zu verlassen. Beispiel: int product = 1; for ( int i = 1; i < 20; i++) { product *= i; if ( product > 1000000 ) { break; } } System.out.println(product); Oliver Haase Hochschule Konstanz 32 Break Merke: break kann immer durch geeignete Schleifenbedingung ersetzt werden! Beispiel: int i = 1; int product = 1; do { product *= i; i++; } while ((i < 20) & ( product <= 1000000)); System.out.println(product); Oliver Haase Hochschule Konstanz 33 Continue Springt zum innersten Schleifenkopf Sinnvoll, um Schleifenausführung für bestimmte Sonderfälle zu vermeiden. Beispiel: for ( int i = -10; i <= 10; i++ ) { if ( i == 0 ) { continue; } System.out.println("1/" + i + "= " + 1/i); } Oliver Haase Hochschule Konstanz 34