Kontrollstrukturen
Block
- Wie in Java, werden Anweisungen, die zwischen zwei geschweiften Klammern
{}stehen als Block bezeichnet. Ein Block kann überall dort eingesetzt werden, wo auch ein einzelnes Statement stehen kann (z. B. in Kontrollstrukturen)
int i = 7;
{
/* Dies ist ein Block */
i++;
}
Auswahl (if)
if-Anweisung bietet eine einseitige Auswahl
- Syntax:
if (BEDINGUNG) { TRUE-ZWEIG } - wenn die logische Bedingung erfüllt ist wird der True-Zweig ausgeführt
if (antwort == 42) {
printf("Die Antwort auf alle Fragen");
}
int Da sich Java bei der Syntax und den Kontrollstrukturen stark von C hat beeinflussen lassen, sollte es nicht verwunden, dass die C-Kontrollstrukturen denen von Java exakt gleichen. (Nur ist es natürlich in Wirklichkeit genau anders herum.)
Ein ganz großer Unterschied zu Java ist der Typ der Bedingung: Während in Java die Bedingung vom Datentyp boolean sein muss, ist sie in C vom Datentyp int. Der Grund ist, dass es in C keinen Datentyp für Wahrheitswerte gibt, sondern diese einfach als int dargestellt werden. Hierbei gilt, dass
0→ false!= 0→ true
Dieses Manko wurde im Standard adressiert und ein neuer Datentyp _Bool wurde in C99 eingeführt. Er heißt _Bool, weil man sich nicht getraut hat, den Datentyp bool zu nennen: Existierende Programme, die sich selbst einen bool definiert haben, hätten sonst nicht mehr compiliert.
Der C11-Standard hat über die Header-Datei stdbool.h einen „echten“ bool eingeführt. In der Datei findet man dann aber auch nur:
#define bool _Bool
#define true 1
#define false 0
Die Kontrollstrukturen fassen aber trotzdem weiterhin jeden Wert != 0 als true und 0 als false auf. Deswegen schreiben C-Programmierer völlig unverkrampft Code wie den folgenden – auch heute noch:
int i = 7;
while (i--) {
printf("%d, ", i);
/* Ausgabe: 6, 5, 4, 3, 2, 1, 0, */
}
if-else-Anweisung bietet eine zweiseitige Auswahl
- Syntax:
if (BEDINGUNG) { TRUE-ZWEIG } else { ELSE-ZWEIG } - wenn die logische Bedingung erfüllt ist, wird der True-Zweig ausgeführt, andernfalls der Else-Zweig
if (antwort == 42) {
printf("Die Antwort auf alle Fragen");
} else {
printf("Keine Antwort");
}
Konventionen für Formatierung von if
- Leerzeichen zwischen
ifund( - Leerzeichen zwischen
)bzw.elseund{ - Kein Leerzeichen zwischen
(bzw.)und Bedingung elsein selbe Zeile wie}oder direkt darunter- True- und False-Zweig werden vier Leerzeichen eingerückt
Mehrfachverzweigung mit if-else-if
if-else-if-Anweisung bietet eine Mehrfachverzweigung
- Eigentlich nur eine Verschachtlung mehrerer if-else-Anweisungen
- Wird speziell formatiert, um besser lesbar zu sein
- Beliebig viele
else if, nur einelse
if (BEDINGUNG1) {
TRUE-ZWEIG1
} else if (BEDINGUNG2) {
TRUE-ZWEIG2
} else if (BEDINGUNG3) {
TRUE-ZWEIG3
} else {
ELSE-ZWEIG
}
if (temp < 10) {
printf("Viel zu kalt");
}
else if (temp < 20) {
printf("Kalt");
}
else if (temp > 50) {
printf("Viel zu warm");
}
else if (temp > 30) {
printf("Warm");
}
else {
printf("Alles super");
}
Die Reihenfolge der Bedingungen ist natürlich entscheidend, da nach dem ersten Treffer die anderen Zweige nicht mehr betrachtet werden. Es wäre also falsch, die ersten beiden Bedingungen zu tauschen, weil dann sowohl bei 9 als auch bei 19 Grad immer die Ausgabe „Kalt“ gemacht würde.
if (temp < 20) {
printf("Kalt");
}
else if (temp < 10) {
/* Wird nie erreicht! */
printf("Viel zu kalt");
}
Mehrfachverzweigung mit switch
switch-Anweisung bietet andere übersichtlichere Form der Mehrfachverzweigung
switch (VARIABLE) {
case WERT1:
ANWEISUNGEN;
break;
case WERT2:
ANWEISUNGEN;
break;
...
default:
ANWEISUNGEN;
}
Besonderheiten
VARIABLE: muss eine Ganzzahl (int) seinWERT: ein möglicher Wert der Variable als Literal oder Konstantedefault: Zweig der ausgeführt wird, wenn kein Wert vorher gepasst hat
switch (monat) {
case 2: tage = 28; break;
case 4: tage = 30; break;
case 6: tage = 30; break;
case 9: tage = 30; break;
case 11: tage = 30; break;
default: tage = 31;
}
Die Variable, die im switch-Statement verwendet wird (hier monat) muss entweder zuweisungskompatibel mit dem Typ int sein (short, char, int) oder es muss sich um einen Aufzählungstyp handeln (enum). Es ist nicht möglich im switch-Statement Variablen von anderen Typen zu verwenden, d. h. double, float etc. sind nicht verwendbar.
Die Werte in den case-Ästen müssen Konstanten vom Typ int sein. Sie brauchen keinen expliziten Block zu schreiben, d. h. anders als beim if bezieht sich der case-Ast auf alle Statements bis zum nächsten case oder break. Gleichzeitig entsteht aber kein neuer Scope und das case muss ein Statement enthalten. Wollen Sie in einem case eine Variable definieren und dort verwenden, müssen Sie einen Block einsetzen.
#include <stdio.h>
int main() {
int selection = 0;
switch (selection) {
case 0: {
/* Block, damit Deklaration von k möglich wird */
int k = 1;
k--;
printf("%d\n", k);
break;
}
case 1:
printf("%d\n", 1);
}
}
Der default-Ast wird ausgeführt, wenn keine der Case-Konstanten gepasst hat.
Bei C fällt bei einem switch-Statement der Kontrollfluss durch alle case-Statements, die auf das case folgen, bei dem die Bedingung zutraf. Daher muss man immer explizit ein break; hinter die case-Statements schreiben, wenn man dieses Verhalten nicht möchte, was fast immer der Fall ist.
Konventionen für Formatierung von switch
- Leerzeichen zwischen
switchund( - Leerzeichen zwischen
)und{ - Kein Leerzeichen zwischen
(bzw.)bei Variable casejeweils um vier Leerzeichen eingerückt- Anweisungen hinter
:voncaseoder darunter, acht Leerzeichen eingerückt
Fragezeichen-Operator
- Bedingungen mit
ifundcasekönnen nicht in Ausdrücken eingesetzt werden - Für die Verwendung in Ausdrücken gibt es einen speziellen Bedingungsoperator (Fragezeichen-Operator)
- Einziger Operator in C mit drei Operanden (ternärer Operator)
- Syntax:
BEDINGUNG ? TRUE-WERT : FALSE-WERT
Beispiel: Absolutbetrag
if (a < 0) {
b = -a;
}
else {
b = a;
}
b = a < 0 ? -a : a;
while-Schleife
- while-Schleife (while loop) prüft vor jedem Durchlauf eine Bedingung (Vergleichsausdruck)
- kopfgesteuert und abweisend
- Syntax:
while (BEDINGUNG) { ANWEISUNGEN }
while (antwort < 42) {
antwort++;
}
Der Rumpf der while-Schleife wird nur betreten, wenn die Bedingung des Ausdrucks wahr ist. Wenn man eine Schleife wünscht, die mindestens einmal durchlaufen wird, muss man die do/while-Schleife wählen. Die Variable auf die getestet wird sollte richtig initialisiert sein und man sollte nicht vergessen sie im Schleifenrumpf zu verändern, da man andernfalls in einer Endlosschleife landet.
Wenn die Anzahl der Schleifendurchläufe schon im Voraus feststeht, sollten Sie die for-Schleife verwenden. Das hier gewählte Beispiel ist also nicht optimal, da man es besser mit for implementiert hätte. while ist gut geeignet für Schleifen, bei denen währende des Durchlaufens erst festgestellt werden kann wann der Abbruch erfolgen soll, z. B. bei linearen Suchen etc.
/* Beispiel: Gib alle Zahlen von 1 bis 10 aus */
int i = 1;
while (i <= 10) {
printf("%d\n", i);
i++;
}
/* Beispiel: Berechne Summe der Zahlen von 1 bis 10 */
int i = 1;
int summe = 0;
while (i <= 10) {
summe += i;
i++;
}
printf("%d\n", summe);
do-while-Schleife
do-while-Schleife (do-loop) ist fußgesteuert (nicht abweisend)
- Syntax:
do { ANWEISUNGEN } while (BEDINGUNG);
do {
n = readNatuerlicheZahl();
} while (n >= 0);
Der Rumpf der do/while-Schleife wird immer mindestens einmal betreten, da der Test erst am Ende erfolgt. Wenn man eine Schleife wünscht, die die Bedingung vorher testet, muss man die while-Schleife verwenden. Die Variable auf die getestet wird sollte richtig initialisiert sein und man sollte nicht vergessen sie im Schleifenrumpf zu verändern, da man andernfalls in einer Endlosschleife landet.
Wenn die Anzahl der Schleifendurchläufe schon im Voraus feststeht, sollten Sie die for-Schleife verwenden. Das hier gewählte Beispiel ist also nicht optimal, da man es besser mit for implementiert hätte.
/* Beispiel: Gib alle Zahlen von 1 bis 10 aus */
int i = 1;
do {
printf("%d\n", i);
i++;
} while (i <= 10);
/* Beispiel: Berechne Summe der Zahlen von 1 bis 10 */
int i = 1;
int summe = 0;
do {
summe += i;
i++;
} while (i <= 10);
printf("%d\n", summe);
for-Schleife
for-Schleife (for-loop) ist kopfgesteuerte (abweisende Schleife)
- bietet mehr Möglichkeiten als die while-Schleife
- Syntax:
for (INIT; BEDINGUNG; UPDATE) { ANWEISUNGEN } INIT: initialisiert Variablen (optional)BEDINGUNG: Schleife läuft, solange die Bedingung wahr istUPDATE: Operationen, die nach jedem Durchlauf durchgeführt werden
int i;
for (i = 0; i < 10; i++) {
/* tu was */
}
Wenn man Java gewöhnt ist, wird man sich wundern, warum die Variable i nicht in der for-Schleife deklariert wird. Der Grund liegt darin, dass bis zum C99-Standard Variablen nur am Anfang eines Blocks deklariert werden durften. Erst mit C99 kam die Möglichkeit, Variablen auch an anderer Stelle, z. B. in dem Initialisierungsteil einer for-Schleife erstmals einzuführen. Der vorhergehende Standard (ANSI-C bzw. C89) kannte diese Erweiterung noch nicht.
Wenn Sie nicht wissen, auf welcher Plattform, mit welchem C-Compiler Ihr Code später einmal compiliert werden wird, sollten Sie auf die Deklaration im for verzichten.
/* Beispiel: Gib alle Zahlen von 1 bis 10 aus */
int i = 0;
for (i = 1; i <= 10; i++) {
printf("%d\n", i);
}
/* Beispiel: Berechne Summe aller Zahlen von 1 bis 10 */
int summe = 0;
int i;
for (i = 1; i <= 10; i++) {
summe += i;
}
printf("%d\n", summe);
Schleifenabbruch
Aktueller Schleifendurchlauf kann abgebrochen werden
- break; – Schleife wird ganz verlassen
(Sprung an die Anweisung nach der Schleife) - continue; – Beginnt sofort einen neuen Schleifendurchlauf
(Sprung in die Prüfung der Bedingung)
Wird häufig als schlechter Programmierstil angesehen. Alternativen
- boolesche Kontrollvariable als Ersatz für
break - zusätzliches
ifals Ersatz fürcontinue
/* Suche kleinste Zahl, die durch 8 und 14 teilbar ist (mit break) */
int i;
for (i = 1; i <= 8*14; i++) {
if ((i % 8 == 0) && (i % 14 == 0)) {
printf("%d\n", i);
break;
}
}
/* Suche kleinste Zahl, die durch 8 und 14 teilbar ist (ohne break) */
int gefunden = 0;
int i;
for (i = 1; i <= 8*14 && !gefunden; i++) {
if ((i % 8 == 0) && (i % 14 == 0)) {
printf("%d\n", i);
gefunden = 1;
}
}
Das goto
- Anders als Java besitzt C noch einen
gotoBefehl goto LABELspringt zum LabelLABEL- Kann nur innerhalb derselben Funktion angewandt werden
int i = 0;
jump_here:
/* Schleife mit goto. Pfui! */
if (i < 5) {
printf("i=%d\n", i);
i++;
goto jump_here;
}
In Java ist goto zwar als Schlüsselwort reserviert aber nicht in der Sprache implementiert. In C hingegen hat es eine Funktion.
Über Funktionsgrenzen hinweg kann in C mit der Funktion _longjmp gesprungen werden.
Codekonventionen
- Hinter
do,forundwhileein Leerzeichen - Vor und hinter den Operatoren ein Leerzeichen
- falls Blockanweisung:
)und{in dieselbe Zeile, durch Leerzeichen getrennt}unter i vonifbzw. w vonwhile- innere Anweisungen um 4 Spalten einrücken
- geschachtelte Schleifen vermeiden (→ Prozeduren)