5 Kontrollstrukturen
5.1 Auswahl
5.1.1 "if"-Anweisung
5.1.2 "switch"-Anweisung
5.2 Schleifen
5.2.1 "while"-Anweisung
5.2.2 "do-while"-Anweisung
5.2.3 "for"-Anweisung
5.3 Vorzeitiger Abbruch und Sprünge
5.3.1 "continue"-Anweisung
5.3.2 "break"-Anweisung
5.3.3 exit()-Funktion
5.3.4 Sprünge
Kontrollstrukturen dienen dazu, ein Problem übersichtlich
und strukturiert zu formulieren. Die wichtigsten Kontrollstrukturen
sind die Auswahl (Entscheidungen) und die Schleifen. Dazu kommen
noch einige andere Kontrollanweisungen.
5.1 Auswahl
5.1.1 "if"-Anweisung
- die "if"-Anweisung dient zur Auswahl unter genau zwei
Möglichkeiten
- sie hat folgenden Aufbau:
if (Ausdruck) {
Anweisung(en)1
}
else {
Anweisung(en)2
}
|
- der else-Teil ist optional, kann also auch fehlen
- hinter (Ausdruck) steht kein Semikolon
- mehrere abhängige Anweisungen müssen in geschweifte
Klammern eingeschlossen werden (Blockbildung)
- Ausdruck wird, wenn es sich nicht um eine Bedingung handelt,
numerisch bewertet (also gleich 0 = FALSCH und ungleich 0 = WAHR),
daher sind die folgenden beiden if-Konstruktionen äquivalent.
Bei der Entscheidung, welche man verwenden will, sollte man auf
gute Dokumentation und leichte Lesbarkeit achten
if (i % k)
printf("Division mit Rest\n");
|
if (i % k != 0)
printf("Division mit Rest\n");
|
- noch 2 Beispiele zur Verwendung von if und if-else:
if (punkte > 50)
printf("Bestanden\n");
else
printf("Nicht Bestanden\n");
|
- es können auch else-if Ketten gebildet werden, else
gehört dabei immer zum jeweils vorangehenden if, ggf.
müssen daher Anweisungen mittels geschweifter Klammern zusammengefaßt
werden
if (punkte > 50)
printf("Note 1\n");
else if (punkte > 40)
printf("Note 2\n");
else if (punkte > 30)
printf("Note 3\n");
else if (punkte > 20)
printf("Note 4\n");
else
printf("Nicht Bestanden\n");
|
Im folgenden Programmstück steckt ein Fehler:
if (n > 0)
for (i = 0; i > n; i++)
if (s[i] > 0) {
printf("...");
return (i);
}
else /* hier ist ein Fehler */
printf("Fehler - n ist kleiner gleich 0\n")
|
Hier ist es nun richtig:
if (n > 0) {
for (i = 0; i > n; i++)
if (s[i] > 0) {
printf("...");
return (i);
}
}
else /* jetzt ist es richtig */
printf("Fehler - n ist kleiner gleich 0\n")
|
Beispielprogramm p5-1.c
/*
* p5-1.c
* Beispielprogramm 1, Abschnitt 5
* Binaeres Suchen von x im
* sortierten Feld v[0] .. v[n-1]
*/
int binary(int x, int v[], int n)
{
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if (x < v[mid])
high = mid - 1;
else if (x > v[mid])
low = mid + 1;
else /* gefunden */
return (mid);
}
return (-1); /* Fehlerkennung */
} /* binary() */
|
5.1.2 "switch"-Anweisung
Zur Mehrfachauswahl ist es anstelle von else-if Ketten
oft günstiger die Kontrollstruktur, die für eine solche
Mehrfachauswahl vorgesehen ist, zu verwenden. In C heißt
diese Kontrollstruktur switch-case. Sie hat folgenden Aufbau:
switch (Ausdruck) {
case Konstante1:
Anweisung(en)1
case Konstante2:
Anweisung(en)2
case Konstante3:
Anweisung(en)3
.
.
.
default:
Anweisung(en)
}
|
- die Konstanten müssen ganzzahlig bzw. Zeichenkonstanten
sein
- default ist optional und wird durchlaufen, wenn keine
der anderen Bedingungen zutrifft.
- es können beliebig viele case-Teile vorhanden sein
- die Abarbeitung und Überprüfung geht vom ersten case-Teil
bis zum letzten
- nach der 1. Übereinstimmung wird der gesamte Rest der switch-Anweisung
durchlaufen, insbesondere auch alle Anweisungen der nachfolgenden
case-Teile !!!
- daher wird man normalerweise immer eine break-Anweisung
am Ende eines case-Teils verwenden
- hier ist noch einmal das Beispiel mit der else-if Kette
von oben, jetzt aber mittels switch gelöst:
punkte = ((punkte-1) / 10) * 10; /* erzeugt: 0, 10, 20, 30 .. */
switch (punkte) {
case 50:
printf("Note 1\n");
break;
case 40:
printf("Note 2\n");
break;
case 30:
printf("Note 3\n");
break;
case 20:
printf("Note 4\n");
break;
default:
printf("Nicht bestanden\n");
}
|
Beispielprogramm p5-2.c
/*
* p5-2.c
* Beispielprogramm 2, Abschnitt 5
* Ein Menugeruest
*/
void main(void)
{
int c;
printf(" HAUPTMENUE\n\n");
printf("A Assembler\n");
printf("C C-Compiler\n");
printf("D DBASE III\n");
printf("T Turbo-PASCAL\n");
printf("\nBitte geben Sie Ihre Wahl ein: ");
switch (c = getchar()) {
case 'a':
case 'A':
printf("\nSie haben Assembler gewaehlt\n");
break;
case 'c':
case 'C':
printf("\nSie haben den C-Compiler gewaehlt\n");
break;
case 'd':
case 'D':
printf("\nSie haben DBASE III gewaehlt\n");
break;
case 't':
case 'T':
printf("\nSie haben Turbo-PASCAL gewaehlt\n");
break;
default:
printf("\nFalsche Wahl !!! \07\n");
}
} /* main() */
|
5.2 Schleifen
5.2.1 "while"-Anweisung
- die while-Anweisung realisiert eine Schleife in der meist
benötigten Art mit der Prüfung der Schleifenbedingung
vor jedem (auch dem ersten) Schleifendurchlauf
- sie sieht so aus:
while (Ausdruck) {
Anweisung(en)
}
|
- die Schleife wird solange wiederholt, wie der Ausdruck WAHR
d.h. ungleich 0 ist
- eine (so natürlich nicht sinnvolle) unendliche Schleife
wäre:
while (1) {
Anweisung(en)
}
|
Beispiele für die Verwendung von while-Schleifen:
int c;
while ((c = getchar()) != EOF)
printf("Zeichen war nicht EOF %c\n", c);
printf("Zeichen war EOF\n");
|
int c;
char s[80];
i = 0;
while ((c = getchar()) != EOF && i < 80 - 1) {
s[i] = c;
i++;
}
s[i]='\0';
|
5.2.2 "do-while"-Anweisung
- die do-while Schleife überprüft die Schleifenbedingung
nach jedem einzelnen Schleifendurchlauf
- eine solche Schleife wird also immer mindestens einmal durchlaufen
- dieser Anwendungsfall ist selten im Vergleich mit der while-Schleife
(nur etwa 5% der Schleifen sind do-while-Schleifen)
- die Struktur der "do-while" Anweisung ist wie
folgt:
do {
Anweisung(en)
} while (Ausdruck);
|
- beachten Sie das Semikolon!
Beispiel:
void main(void) /* "Lobmaschine" */
{
int c;
do {
printf("\nDas hast Du prima gemacht !");
printf("\nNochmal wiederholen (J/N) ?");
c = getchar();
} while (c == 'j' || c == 'J');
} /* main() */
|
5.2.3 "for"-Anweisung
- eine in C sehr häufig verwendete Schleife ist die for-Schleife
- sie entspricht einer while-Schleife mit Initialisierung
und Abschlußanweisung(en)
- die Struktur der Anweisung sieht so aus:
for (Ausdruck1; Ausdruck2; Ausdruck3) {
Anweisung(en)
}
|
- Ausdruck1 stellt die Initialisierung dar, er wird nur
einmal vor dem ersten Schleifendurchlauf abgearbeitet
- Ausdruck2 ist die Schleifenbedingung, die vor jedem Schleifendurchlauf
bewertet wird
- Ausdruck3 ist die Reinitialisierung der Schleife, er
wird am Ende der Schleife unmittelbar vor der Bewertung der Bedingung
ausgeführt
- jeder der 3 Ausdrücke kann fehlen, der Weglasswert für
den Ausdruck2 ist WAHR!
- Ausdrücke können auch Ausdruckslisten (Kommaoperator)
sein
- äquivalent zur for-Schleife ist folgende Konstruktion
mit while:
Ausdruck1
while (Ausdruck2) {
Anweisung(en)
Ausdruck3
}
|
- die folgende for-Anweisung realisiert eine unendliche
Schleife:
for (;;) {
Anweisung(en)
}
|
- die übliche Schleife für die Abarbeitung eines eindimensionalen
Feldes (Vektor) sieht so aus:
for (i = 0; i < N; i++) {
Anweisung(en)
}
|
- rückwärts:
for (i = N - 1; i >= 0; i--) {
Anweisung(en)
}
|
Beispielprogramm p5-3.c (siehe auch p4-9.c)
/*
* p5-3.c
* Beispielprogramm 3, Abschnitt 5
* wandelt die naechsten x Ziffern aus dem
* String s in eine Integerzahl
*/
int atoi(char s[])
{
int i, n, sign;
/* fuehrende Zwischenraeume ueberlesen */
for (i = 0; s[i]==' ' || s[i]=='\n' || s[i]=='\t'; i++) {
;
/* Vorzeichen */
sign = 1;
if (s[i] == '+' || s[i] == '-')
sign = (s[i++] == '+') ? 1 : -1;
/* Zahl bilden */
for (n = 0; s[i] >= '0' && s[i] <= '9'; i++)
n = 10 * n + s[i] - '0';
return (sign * n);
} /* atoi() */
|
Beispielprogramm p5-4.c
/*
* p5-4.c
* Beispielprogramm 4, Abschnitt 5
* Shell-Sort (aufsteigend) fuer Vektor v
*/
void shell(int v[], int n)
{
int gap, i, j, temp;
for (gap = n / 2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j = i - gap; j >= 0 && v[j] > v[j+gap]; j -= gap) {
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
} /* shell() */
|
Beispielprogramm p5-5.c
/*
* p5-5.c
* Beispielprogramm 5, Abschnitt 5
* String s umkehren
*/
void reverse(char s[])
{
int c, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
} /* reverse() */
|
5.3 Vorzeitiger Abbruch und Sprünge
5.3.1 "continue"-Anweisung
- dient zum vorzeitigen Abbruch eines Schleifendurchlaufes
der innersten while-, do-while- oder for-Schleife
- bei while und do-while wird dann sofort die Schleifenbedingung
geprüft, bei for wird die Reinitialisierung vorgenommen
- die "continue" Anweisung ist für C nicht
unbedingt notwendig, ihre Wirkung kann immer auch ohne continue
durch eine andere Schachtelung erreicht werden
Beispiel:
/* mit continue */
for (i = 0; i < N; i++) {
if (a[i] < 0) /* negative Elemente überspringen */
continue;
... /* nicht negative Elemente bearbeiten */
}
|
/* ohne continue */
for (i = 0; i < N; i++) {
if (a[i] > 0) { /* negative Elemente überspringen */
... /* nicht negative Elemente bearbeiten */
}
}
|
5.3.2 "break"-Anweisung
- diese Anweisung haben wir schon im Zusammenhang mit der switch-Anweisung
kennengelernt
- sie dient zum Verlassen der innersten while-,
do-while- oder for-Schleife, bzw. der switch-Anweisung
Beispielprogramm p5-6.c
/*
* p5-6.c
* Beispielprogramm 6, Abschnitt 5
* Zwischenraeume am Zeilenende entfernen
*/
#define MAXLINE 1000
void main(void)
{
int n;
char line[MAXLINE];
while ((n = getline(line, MAXLINE)) > 0) {
while (--n >= 0)
if (line[n] != ' ' && line[n] != '\t' && line[n] != '\n')
break;
line[n + 1] = '\0';
printf("%s\n", line);
}
} /* main() */
|
5.3.3 exit()-Funktion
- die exit() Funktion dient zum Verlassen (Beenden) des
gesamten Programmes
- sie hat die Form:
- Parameter muß vom Typ int sein, der Zahlenwert
wird vom Betriebssystem bzw. allgemeiner vom Vaterprozeß
ausgewertet (bei MS-DOS mit ERRORLEVEL)
- es gilt die Konvention: Parameter = 0 ==> Programmlauf okay,
sonst Parameter gleich Fehlernummer
Beispielprogramm p5-7.c
/*
* p5-7.c
* Beispielprogramm 7, Abschnitt 5
* Ja-Nein Abfrage
*/
void main(void)
{
int c;
printf("\nWeitermachen (J/N) ? ");
c = getchar();
if (c == 'j' || c == 'J')
exit(0);
else
exit(1);
} /* main()*/
|
5.3.4 Sprünge
- in seltenen Fällen kann es angebracht sein,
Sprünge zu verwenden
- C stellt dazu eine Sprunganweisung zur Verfügung:
goto marke; oder marke:
. .
. .
. .
marke: goto marke;
|
- marke ist ein frei gewählter Name
- das Sprungziel muß sich in derselben Funktion wie die
Sprunganweisung befinden
- vor jeder Anweisung dürfen beliebig viele Sprungmarken
stehen
- Sprünge sollten vermieden werden !!!
zurück zum Inhaltsverzeichnis