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:


  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

 
  /* 
   *      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

 

11. November 1999, Peter Klingebiel, DVZ