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.
- 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 folgende 2 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"); 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
binary(x,v,n) /* binäres Suchen von x im sortierten Feld v[0] ... v[n-1] */ int x,v[],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 */ }
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
main() /* ein Menuegeruest */ { 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 gewählt\n"); break; case 'c': case 'C': printf("\nSie haben den C-Compiler gewählt\n"); break; case 'd': case 'D': printf("\nSie haben DBASE III gewählt\n"); break; case 't': case 'T': printf("\nSie haben Turbo-PASCAL gewählt\n"); break; default: printf("\nFalsche Wahl !!! \07\n"); } }
- 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; /* oder: s[i++]=c; das erspart i++; die geschweiften Klammern */ } s[i]='\0';
- 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:
main() /* "Lobmaschine" */ { int c; do { printf("\nDas hast Du prima gemacht !"); printf("\nNochmal wiederholen (J/N) ?"); c=getchar(); } while (c=='j' || c=='J') ; }
- 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++)
- rückwärts:
for (i=N-1; i>=0; i--)
Beispielprogramm P5-3.C (siehe auch P4-8.C)
atoi(s) /* Ziffern der Zeichenkette s in int wandeln */ char s[]; { int i,n,sign; for (i=0; s[i]==' ' || s[i]=='\n' || s[i]=='\t'; i++) ; /* führende Zwischenräume überlesen */ sign = 1; if (s[i]=='+' || s[i]=='-') /* Vorzeichen */ sign = (s[i++]=='+') ? 1 : -1; for (n=0; s[i] >='0' && s[i] <='9'; i++) n = 10*n + s[i] - '0'; return (sign*n); }
Beispielprogramm P5-4.C
shell(v,n) /* Shell Sort (aufsteigend) für den Vektor v */ int v[],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; } }
Beispielprogramm P5-5.C
reverse(s) /* Vektor s umkehren */ 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; } }
5.3 Vorzeitiger Abbruch und Sprünge
- 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 */ } }
- 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
#define MAXLINE 1000 main() /* entfernt Zwischenraeume am Zeilenende */ { 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); } }
- die exit() Funktion dient zum Verlassen (Beenden) des gesamten Programmes
- sie hat die Form:
exit (Parameter) ;
- 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
main() /* Ja-Nein Abfrage */ { int c; printf("\nWeitermachen (J/N) ? "); c = getchar(); if (c=='j' || c=='J') exit(0); else exit(1); }
- 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 !!!