Am Tag 5, »Grundlagen der Programmsteuerung,« haben Sie bereits einige
Kontrollanweisungen von C kennen gelernt, mit deren Hilfe die Ausführung anderer
Anweisungen im Programm gesteuert werden kann. Heute werden wir uns mit weiter
fortgeschrittenen Aspekten der Programmsteuerung, einschließlich der goto
-
Anweisung und einiger interessanterer Anwendungen von Schleifen, beschäftigen.
Heute lernen Sie:
break
- und continue
-Anweisungen verwendet
goto
-Anweisung verbirgt und warum Sie sie vermeiden sollten
switch
-Anweisung verwendet
Am Tag 5 haben Sie gelernt, wie Sie mit der for
-, der while
- und der do...while
-
Schleife die Programmausführung kontrollieren können. Diese
Schleifenkonstruktionen führen einen Block von C-Anweisungen nie, einmal oder
mehr als einmal aus, je nachdem ob bestimmte Bedingungen in dem Programm erfüllt
werden. In allen drei Fällen wird die Schleife nur beendet oder verlassen, wenn eine
bestimmte Bedingung zutrifft.
Es kann jedoch vorkommen, dass Sie mehr Einfluss auf die Ausführung der Schleife
nehmen wollen. Die Anweisungen break
und continue
bieten diese Möglichkeit der
Einflussnahme.
Die break
-Anweisung kann nur in dem Rumpf einer for
-, while-
oder do...while
-
Schleife verwendet werden. (Sie kann darüber hinaus auch in switch
-Anweisungen
verwendet werden, doch dazu komme ich erst weiter hinten in diesem Kapitel.) Wenn
das Programm auf eine break
-Anweisung trifft, wird die Ausführung der Schleife
sofort abgebrochen. Sehen Sie dazu folgendes Beispiel:
for ( count = 0; count < 10; count++ )
{
if ( count == 5 )
break;
}
Die for
-Schleife ohne die break
-Anweisung würde 10-Mal ausgeführt. Beim sechsten
Durchlauf ist jedoch count
gleich 5,
und die break
-Anweisung wird ausgeführt. Sie
bewirkt, dass die for
-Schleife abgebrochen wird. Die Ausführung geht damit an die
Anweisung über, die direkt auf die schließende geschweifte Klammer der for
-Schleife
folgt. Wenn eine break
-Anweisung innerhalb einer verschachtelten Schleife steht, tritt
das Programm lediglich aus der innersten Schleife aus.
Listing 12.1 veranschaulicht die Verwendung einer break
-Anweisung.
Listing 12.1: Beispiel für eine break-Anweisung.
1: /* Beispiel für eine break-Anweisung. */
2:
3: #include <stdio.h>
4:
5: char s[] = "Dies ist ein Test-String. Er enthält zwei Sätze.";
6:
7: int main(void)
8: {
9: int count;
10:
11: printf("\nOriginal-String: %s", s);
12:
13: for (count = 0; s[count]!='\0'; count++)
14: {
15: if (s[count] == '.')
16: {
17: s[count+1] = '\0';
18: break;
19: }
20: }
21: printf("\nGeänderter String: %s\n", s);
22:
23: return 0;
24: }
Original-String: Dies ist ein Test-String. Er enthält zwei Sätze.
Geänderter String: Dies ist ein Test-String.
Dieses Programm extrahiert den ersten Satz aus einem String. Es durchsucht den
String zeichenweise nach dem ersten Punkt (der das Satzende markieren sollte). Dies
geschieht in der for
-Schleife in den Zeilen 13 bis 20. Zeile 13 startet die for
-Schleife
und inkrementiert count
, so dass count
als Index auf die Zeichen in dem String s
fungieren kann. Zeile 15 überprüft, ob das aktuelle Zeichen in dem String ein Punkt
ist. Wenn ja, wird direkt nach dem Punkt ein Nullzeichen eingefügt (Zeile 17). Damit
wird der String abgeschnitten. Nachdem Sie den String abgeschnitten haben,
benötigen Sie keinen weiteren Schleifendurchlauf mehr, wird die Schleife mit einer
break
-Anweisung (Zeile 18) schnell beendet und die Programmausführung mit der
ersten Zeile nach der Schleife fortgesetzt (Zeile 21). Wird kein Punkt gefunden, bleibt
der String unverändert.
Eine Schleife kann mehrere break
-Anweisungen enthalten, von denen aber immer nur
eine break
-Anweisung ausgeführt wird (falls überhaupt). Wenn kein break
ausgeführt
wird, endet die Schleife normal (entsprechend ihrer Testbedingung). Abbildung 12.1
zeigt den Ablauf der break
-Anweisung.
Abbildung 12.1: Der Ablauf der break- und continue-Anweisung.
break;
break
wird innerhalb einer Schleife oder einer switch
-Anweisung verwendet. Die
break
-Anweisung bewirkt, dass die aktuelle Schleife (for
, while
oder do...while
) oder
switch
-Anweisung unmittelbar verlassen wird, d.h. es gibt keine weiteren
Schleifendurchläufe, und das Programm wird mit der ersten Anweisung nach der
Schleife oder der switch
-Anweisung forgesetzt.
int x;
printf ( "Zählt von 1 bis 10\n" );
/* ohne Abbruchbedingung in der Schleife würde diese endlos durchlaufen */
for( x = 1; ; x++ )
{
if( x == 10 ) /* Prüft, ob der Wert gleich 10 ist */
break; /* Beendet die Schleife */
printf( "\n%d", x );
}
Wie die break
-Anweisung kann auch die continue
-Anweisung nur in dem Rumpf einer
for
-, einer while-
oder einer do...while
-Schleife verwendet werden. Die Ausführung
einer continue
-Anweisung bewirkt, dass der aktuelle Schleifendurchlauf abgebrochen
und direkt der nächste Durchlauf der umschließenden Schleife begonnen wird. Die
Anweisungen zwischen der continue
-Anweisung und dem Ende der Schleife werden
dabei übersprungen. Wie continue
den Programmablauf verändert, ist in Listing 12.2
dargestellt. Achten Sie dabei auf die Unterschiede zu der break
-Anweisung.
Das Programmbeispiel in Listing 12.2 liest eine über die Tastatur eingegebene Zeile ein und zeigt sie an, nachdem alle kleingeschriebenen Vokale entfernt wurden.
Listing 12.2: Beispiel für eine continue-Anweisung.
1: /* Beispiel für eine continue-Anweisung. */
2:
3: #include <stdio.h>
4:
5: int main(void)
6: {
7: /* Deklariert den Puffer für die Eingabe und eine Zählervariable. */
8:
9: char puffer[81];
10: int ctr;
11:
12: /* Einlesen einer Textzeile. */
13:
14: puts("Geben Sie eine Textzeile ein:");
15: fgets(puffer,81,stdin);
16:
17: /* Durchläuft den String und zeigt nur die Zeichen an, */
18: /* die keine kleingeschriebenen Vokale sind. */
19:
20: for (ctr = 0; puffer[ctr] !='\0'; ctr++)
21: {
22:
23: /* Ist das Zeichen ein kleingeschriebener Vokal, gehe zurück */
24: /* an den Anfang der Schleife, ohne ihn anzuzeigen. */
25:
26: if (puffer[ctr] == 'a' || puffer[ctr] == 'e'
27: || puffer[ctr] == 'i' || puffer[ctr] == 'o'
28: || puffer[ctr] == 'u')
29: continue;
30:
31: /* Nur Nichtvokale anzeigen. */
32:
33: putchar(puffer[ctr]);
34: }
35: return 0;
36: }
Geben Sie eine Textzeile ein:
Dies ist eine Textzeile
Ds st n Txtzl
Auch wenn dieses Programm keinen besonderen praktischen Nutzen hat, zeigt es
doch sehr wirkungsvoll den Einsatz der continue
-Anweisung. Die Zeilen 9 und 10
deklarieren die Variablen des Programms. puffer[]
nimmt den String auf, den der
Anwender in Zeile 15 eingibt. Die andere Variable, ctr
, inkrementiert durch die
Elemente des Arrays puffer[]
, während die for
-Schleife in den Zeilen 20 bis 34 nach
Vokalen sucht. Eine if
-Anweisung in den Zeilen 26 bis 28 prüft für jeden Buchstaben,
ob es sich um einen kleingeschriebenen Vokal handelt. Ist dies der Fall, kommt es zur
Ausführung der continue
-Anweisung, die die Programmausführung zurück zu Zeile
20, der for
-Schleife, schickt. Ist der Buchstabe kein Vokal, wird mit Zeile 33
fortgefahren. Zeile 33 enthält eine neue Bibliotheksfunktion, putchar()
, die ein
einzelnes Zeichen auf dem Bildschirm ausgibt.
continue;
continue
wird innerhalb von Schleifen verwendet. Diese Anweisung veranlasst, dass
der Rest des aktuellen Schleifendurchlaufs übersprungen und das Programm mit dem
nächsten Durchlauf fortgesetzt wird.
int x;
printf("Gibt nur die geraden Zahlen von 1 bis 10 aus\n");
for( x = 1; x <= 10; x++ )
{
if( x % 2 != 0 ) /* Prüft, ob die Zahl nicht gerade ist */
continue; /* Springt zum nächsten Wert für x */
printf( "\n%d", x );
}
Die goto
-Anweisung ist eine der Sprung- oder Verzweigungsanweisungen in C, die
nicht mit einer Bedingung verknüpft sind. Wenn ein Programm auf eine goto
-
Anweisung trifft, springt beziehungsweise verzweigt die Programmausführung direkt
zu der Stelle, die in der goto
-Anweisung spezifiziert wurde. Diese Anweisung bedarf
keiner Bedingung, da die Ausführung immer verzweigt, wenn sie auf eine goto
-
Anweisung trifft. Die Verzweigung hängt nicht von der Erfüllung irgendwelcher
Programmbedingungen ab (wie das zum Beispiel bei if
-Anweisungen der Fall ist).
Das Ziel einer goto
-Anweisung wird mit einer Sprungmarke (Label) gefolgt von einem
Doppelpunkt angegeben. Eine Sprungmarke kann allein in einer Zeile stehen oder am
Anfang einer Zeile, die eine C-Anweisung enthält. Die Sprungmarken eines
Programms müssen eindeutig sein.
Das Ziel einer goto
-Anweisung muss in der gleichen Funktion wie die goto
-Anweisung
stehen, aber nicht notwendigerweise im gleichen Block. Listing 12.3 zeigt ein
einfaches Programm, in dem eine goto
-Anweisung verwendet wird.
Listing 12.3: Beispiel für eine goto-Anweisung.
1: /* Beispiel für eine goto-Anweisung */
2:
3: #include <stdio.h>
4:
5: int main(void)
6: {
7: int n;
8:
9: start:
10:
11: puts("Geben Sie eine Zahl zwischen 0 und 10 ein: ");
12: scanf("%d", &n);
13:
14: if (n < 0 ||n > 10 )
15: goto start;
16: else if (n == 0)
17: goto location0;
18: else if (n == 1)
19: goto location1;
20: else
21: goto location2;
22:
23: location0:
24: puts("Ihre Eingabe lautete 0.\n");
25: goto ende;
26:
27: location1:
28: puts("Ihre Eingabe lautete 1.\n");
29: goto ende;
30:
31: location2:
32: puts("Sie haben einen Wert zwischen 2 und 10 eingegeben.\n");
33:
34: ende:
35: return 0;
36: }
Geben Sie eine Zahl zwischen 0 und 10 ein:
1
Ihre Eingabe lautete 1.
Geben Sie eine Zahl zwischen 0 und 10 ein:
9
Sie haben einen Wert zwischen 2 und 10 eingegeben.
Dies ist ein einfaches Programm, das eine Zahl zwischen 0 und 10 einliest. Wenn die
eingegebene Zahl nicht zwischen 0 und 10 liegt, springt das Programm mit einer
goto
-Anweisung zurück zu start
in Zeile 9. Andernfalls prüft das Programm in Zeile
16, ob die Zahl gleich 0 ist. Wenn ja, lässt eine goto
-Anweisung in Zeile 17 die
Ausführung mit location0
(Zeile 23) fortfahren, woraufhin in Zeile 24 ein Text
ausgegeben und anschließend eine weitere goto
-Anweisung ausgeführt wird. Die goto
-
Anweisung in Zeile 25 bewirkt, dass die Programmausführung zu dem Label ende
,
dem Ende des Programms, springt. Das Programm verfährt für die Eingabe des
Wertes 1
und für alle Werte zwischen 2
und 10
nach den gleichen Regeln.
Die Sprungmarke einer goto
-Anweisung kann entweder vor oder nach der Anweisung
stehen. Die einzige, bereits erwähnte Einschränkung ist, dass goto
und die
Sprungmarke in der gleichen Funktion stehen müssen. Sie können jedoch
unterschiedlichen Blöcken angehören. Mit goto
-Anweisungen können Sie in Schleifen
(beispielsweise for
-Anweisungen) einsteigen oder aus ihnen aussteigen, aber Sie
sollten das möglichst vermeiden. Um ehrlich zu sein, möchte ich Ihnen sogar davon
abraten, je eine goto
-Anweisung irgendwo in Ihren Programmen zu verwenden. Dafür
gibt es zwei Gründe:
goto
ist überflüssig - keine Programmieraufgabe erfordert eine goto
-Anweisung.
Es besteht immer die Möglichkeit, den benötigten Code mit einer der anderen
Verzweigungsanweisungen von C aufzusetzen.
goto
-Anweisung scheint auf den ersten Blick eine optimale
Lösung für bestimmte Programmierprobleme zu sein, aber man kann dabei
unbemerkt auf Abwege geraten. Schnell hat man Code geschrieben, bei dem die
Programmausführung dank goto
wild hin- und herspringt. Das Ergebnis dieser Art
der Programmierung bezeichnet man auch als Spaghetti-Code.
Es gibt Programmierer, die goto
verwenden und dennoch perfekte Programme
schreiben. Es kann auch immer mal Situationen geben, in denen der wohlüberlegte
Einsatz von goto
die einfachste Lösung eines Programmierproblems darstellt. Es ist
jedoch nie die einzige Lösung. Wenn Sie diese Warnung schon ignorieren, sollten Sie
zumindest Vorsicht walten lassen!
Vermeiden Sie | Verwechseln Sie nicht |
goto Ziel;
Ziel
ist eine Label-Anweisung, die die Stelle im Programm identifiziert, zu der die
Ausführung verzweigt. Eine Label-Anweisung besteht aus einem Bezeichner gefolgt
von einem Doppelpunkt und, optional, einer C-Anweisung:
Ziel: eine C Anweisung;
Sie können das Label (die Sprungmarke) auch allein in eine Zeile setzen. Manche Programmierer lassen in einem solchen Fall eine Leeranweisung folgen (ein Semikolon). Dies ist aber nicht notwendig.
Ziel: ;
Was ist eine Endlosschleife und wann bietet es sich an, eine solche Schleife in einem
Programm zu verwenden? Unter Endlosschleifen versteht man Schleifen, die - für sich
genommen - ohne Ende durchlaufen werden. Es kann sich dabei um eine for
-, eine
while-
oder eine do...while
-Schleife handeln. Wenn Sie zum Beispiel
while (1)
{
/* hier steht weiterer Code */
}
schreiben, erzeugen Sie eine Endlosschleife. Die Bedingung, die in der while
-Schleife
getestet wird, ist die Konstante 1
, die immer wahr ist und nicht von dem Programm
geändert werden kann. Da die Bedingung immer erfüllt ist, wird die Schleife nie
beendet.
Im vorigen Abschnitt haben Sie gelernt, dass Sie mit der break
-Anweisung eine
Schleife verlassen können. Ohne break
-Anweisung wäre eine Endlosschleife nutzlos.
Mit break
jedoch können Sie Endlosschleifen zu Ihrem Vorteil nutzen.
Endlosschleifen mit for
oder do...while
werden wie folgt erzeugt:
for (;;)
{
/* hier steht weiterer Code */
}
do
{
/* hier steht weiterer Code */
} while (1);
Das Prinzip ist für alle drei Schleifentypen das Gleiche. Für die Beispiele in diesem
Abschnitt verwenden wir eine while
-Schleife.
Endlosschleifen können beispielsweise verwendet werden, wenn man viele
Abbruchbedingungen zu testen hat. Dabei kann es sich als kompliziert erweisen, alle
Testbedingungen in Klammern nach der while
-Anweisung anzugeben. Unter
Umständen ist es einfacher, die Bedingungen einzeln im Rumpf der Schleife zu testen
und dann bei Bedarf die Schleife mit einem break
zu verlassen.
Eine Endlosschleife kann auch ein Menüsystem erzeugen, das den Ablauf Ihres
Programms steuert. Erinnern wir uns an Tag 4, »Funktionen«, wo wir darüber
gesprochen haben, dass die main()
-Funktion oft als eine Art »Verkehrspolizist«
fungiert, der die Ausführung der verschiedenen Funktionen, die die eigentliche Arbeit
des Programms erledigen, dirigiert. Häufig wird dies durch Menüs unterstützt: Das
Programm präsentiert dem Anwender eine Liste von Optionen, aus denen er eine
auswählen muss (eine der verfügbaren Optionen sollte das Programm beenden).
Nachdem eine Wahl getroffen wurde, sorgt eine geeignete Entscheidungsanweisung
im Programm dafür, dass das Programm entsprechend der Benutzerauswahl
fortgesetzt wird.
Listing 12.4 demonstriert die Verwendung eines Menüsystems.
Listing 12.4: Ein Menüsystem mit einer Endlosschleife implementieren.
1: /* Beispiel für ein Menüsystem, das mit einer */
2: /* Endlosschleife implementiert wird. */
3: #include <stdio.h>
4: #define DELAY 1500000 /* Für Warteschleife. */
5:
6: int menue(void);
7: void warten(void);
8:
9: int main(void)
10: {
11: int option;
12:
13: while (1)
14: {
15:
16: /* Übernimmt die Auswahl des Anwenders. */
17:
18: option = menue();
19:
20: /* Verzweigt auf Basis der Eingabe. */
21:
22: if (option == 1)
23: {
24: puts("\nAufgabe A wird ausgeführt.");
25: warten();
26: }
27: else if (option == 2)
28: {
29: puts("\nAufgabe B wird ausgeführt.");
30: warten();
31: }
32: else if (option == 3)
33: {
34: puts("\nAufgabe C wird ausgeführt.");
35: warten();
36: }
37: else if (option == 4)
38: {
39: puts("\nAufgabe D wird ausgeführt.");
40: warten();
41: }
42: else if (option == 5) /* Ende des Programms. */
43: {
44: puts("\nSie verlassen das Programm...\n");
45: warten();
46: break;
47: }
48: else
49: {
50: puts("\nUngültige Option, versuchen Sie es noch einmal.");
51: warten();
52: }
53: }
54: return 0;
55: }
56:
57: /* Gibt ein Menü aus und liest die Auswahl des Anwenders ein. */
58: int menue(void)
59: {
60: int antwort;
61:
62: puts("\nGeben Sie 1 für Aufgabe A ein.");
63: puts("Geben Sie 2 für Aufgabe B ein.");
64: puts("Geben Sie 3 für Aufgabe C ein.");
65: puts("Geben Sie 4 für Aufgabe D ein.");
66: puts("Geben Sie 5 zum Verlassen des Programms ein.");
67:
68: scanf("%d", &antwort);
69:
70: return antwort;
71: }
72:
73: void warten( void )
74: {
75: long x;
76: for ( x = 0; x < DELAY; x++ )
77: ;
78: }
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
1
Aufgabe A wird ausgeführt.
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
6
Ungültige Option, versuchen Sie es noch einmal.
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
5
Sie verlassen das Programm...
In Zeile 18 wird eine Funktion namens menue()
aufgerufen, die in den Zeilen 58 bis
71 definiert ist. menue()
gibt ein Menü auf den Bildschirm aus, liest die Eingabe des
Anwenders ein und übergibt sie an das Hauptprogramm. In main()
wird der
Rückgabewert von einer Reihe von verschachtelten if
-Anweisungen getestet und die
Programmausführung entsprechend verzweigt. In unserem Beispielprogramm werden
nur Meldungen auf dem Bildschirm ausgegeben. In einem echten, praxisorientierten
Programm würde der Code zur Lösung der jeweiligen Aufgabe wahrscheinlich eine
passende Funktion aufrufen.
Das Programm enthält noch eine zweite Funktion mit Namen warten()
. warten()
wird
in den Zeilen 73 bis 78 definiert und macht eigentlich nicht allzu viel, außer dass die
for
-Anweisung aus Zeile 76 ausgeführt wird. Die for
-Schleife selbst tut auch nichts.
Da sie aber DELAY
-mal ausgeführt wird, sorgt sie für eine gewisse Verzögerung im
Programmablauf.
Diese Art der Verzögerung wird auch als »busy-wait« bezeichnet, da die CPU des Computers für die Dauer der Verzögerung beschäftigt und besetzt ist. Auf einem Multiuser-Betriebssystem mit Multitasking wie Linux wäre es dumm die CPU damit zu beschäftigen, nichts zu tun, wenn sie eigentlich sinnvollere Arbeit erledigen könnte.
Besser ist es daher, unter Linux und anderen Unix-ähnlichen Betriebssystemen die
sleep()
-Funktion zu verwenden. Diese Funktion übergibt die Kontrolle über die CPU für eine bestimmte Zeit (die in Sekunden gemessen und als Argument an die Funktion übergeben wird) zurück an das Betriebssystem. Umsleep()
verwenden zu können, muss Ihr Programm die Header-Dateiunistd.h
einbinden.
Die wohl flexibelste Anweisung zur Steuerung des Programmflusses ist die switch
-
Anweisung. Mit ihr können Sie die weitere Programmausführung von Ausdrücken
abhängig machen, die mehr als zwei Werte annehmen können. Frühere
Programmsteueranweisungen, wie zum Beispiel if
, waren auf boolesche Ausdrücke
beschränkt, die nur einen von zwei Werten annehmen konnten: wahr
oder falsch
.
Um den Programmfluss auf der Basis von mehr als zwei Werten zu steuern, mussten
Sie, wie in Listing 12.4, mehrere verschachtelte if
-Anweisungen verwenden. Mit der
switch
-Anweisung werden solche Verschachtelungen überflüssig.
Die allgemeine Form der switch
-Anweisung lautet:
switch (Ausdruck)
{
case Konstante_1: Anweisung(en);
case Konstante_2: Anweisung(en);
...
case Konstante_n: Anweisung(en);
default: Anweisung(en);
}
In dieser Anweisung ist Ausdruck
ein beliebiger Ausdruck, der zu einem Integer-Wert
des Typs long
, int
oder char
ausgewertet wird. Die switch
-Anweisung wertet
Ausdruck
aus und vergleicht den Wert mit den Konstanten, die auf die case
-Marken
folgen. Diese drei Möglichkeiten ergeben sich:
Ausdruck
und einer der Konstanten
gefunden, springt die Programmausführung zu der Anweisung, die auf die case
-
Marke folgt.
default
-Marke folgt.
default
-Marke,
wird das Programm mit der ersten Anweisung nach der schließenden Klammer
der switch
-Anweisung fortgesetzt.
In Listing 12.5 finden Sie ein Beispiel für eine switch
-Anweisung, die eine Meldung
auf der Basis der Benutzereingabe ausgibt.
Listing 12.5: Beispiel für eine switch-Anweisung.
1: /* Beispiel für eine switch-Anweisung. */
2:
3: #include <stdio.h>
4:
5: int main(void)
6: {
7: int antwort;
8:
9: puts("Geben Sie eine Zahl zwischen 1 und 5 ein:");
10: scanf("%d", &antwort);
11:
12: switch (antwort)
13: {
14: case 1:
15: puts("Ihre Eingabe lautete 1.");
16: case 2:
17: puts("Ihre Eingabe lautete 2.");
18: case 3:
19: puts("Ihre Eingabe lautete 3.");
20: case 4:
21: puts("Ihre Eingabe lautete 4.");
22: case 5:
23: puts("Ihre Eingabe lautete 5.");
24: default:
25: puts("Nicht gültig, versuchen Sie es noch einmal.");
26: }
27:
28: return 0;
29: }
Geben Sie eine Zahl zwischen 1 und 5 ein:
2
Ihre Eingabe lautete 2.
Ihre Eingabe lautete 3.
Ihre Eingabe lautete 4.
Ihre Eingabe lautete 5.
Nicht gültig, versuchen Sie es noch einmal.
Hier liegt doch sicherlich ein Fehler vor? Es scheint, als ob die switch
-Anweisung die
erste übereinstimmende case
-Marke findet und dann alle folgenden Anweisungen
ausführt (und nicht nur die Anweisungen, die mit der case
-Marke verbunden sind).
Und genau das ist hier passiert, denn genau so soll switch
arbeiten (als ob man mit
goto
-Anweisungen zu den übereinstimmenden case
-Marken springen würde). Um
sicherzustellen, dass nur die mit der übereinstimmenden case
-Marke verbundenen
Anweisungen ausgeführt werden, müssen Sie überall wo nötig eine break
-Anweisung
hinzufügen. Listing 12.6 enthält eine Neufassung des Programms mit den
hinzugefügten break
-Anweisungen. Damit funktioniert das Programm
ordnungsgemäß.
Listing 12.6: Korrekte Verwendung von switch einschließlich aller benötigten break-Anweisungen.
1: /* Korrektes Beispiel für eine switch-Anweisung. */
2:
3: #include <stdio.h>
4:
5: int main(void)
6: {
7: int antwort;
8:
9: puts("\nGeben Sie eine Zahl zwischen 1 und 5 ein:");
10: scanf("%d", &antwort);
11:
12: switch (antwort)
13: {
14: case 0:
15: break;
16 case 1:
17: {
18: puts("Ihre Eingabe lautete 1.\n");
19: break;
20: }
21: case 2:
22: {
23: puts("Ihre Eingabe lautete 2.\n");
24: break;
25: }
26: case 3:
27: {
28: puts("Ihre Eingabe lautete 3.\n");
29: break;
30: }
31: case 4:
32: {
33: puts("Ihre Eingabe lautete 4.\n");
34: break;
35: }
36: case 5:
37: {
38: puts("Ihre Eingabe lautete 5.\n");
39: break;
40: }
41: default:
42: {
43: puts("Nicht gültig, versuchen Sie es noch einmal.\n");
44: }
45: } /* Ende der switch-Anweisung */
46: return 0;
47: }
Geben Sie eine Zahl zwischen 1 und 5 ein:
1
Ihre Eingabe lautete 1.
Geben Sie eine Zahl zwischen 1 und 5 ein:
6
Nicht gültig, versuchen Sie es noch einmal.
Kompilieren Sie diese Version und führen Sie sie aus. Jetzt hat alles seine Ordnung.
Häufig wird die switch
-Anweisung dazu verwendet, Menüs wie in Listing 12.4 zu
implementieren. Listing 12.7 verwendet switch
anstelle von if
zur Unterstützung des
Menüs. switch
-Anweisungen eignen sich dazu viel besser als verschachtelte if
-
Anweisungen, wie wir sie in der früheren Version des Menü-Programms (Listing 12.4)
verwendet haben.
Listing 12.7: Realisierung eines Menüs mit Hilfe einer switch-Anweisung.
1 : /* Beispiel für die Implementierung eines Menüsystems mit */
2 : /* einer Endlosschleife und einer switch-Anweisung. */
3 : #include <stdio.h>
4 : #include <stdlib.h>
5 : #include <unistd.h>
6 :
7 : int menue(void);
8 :
9 : int main(void)
10: {
11:
12: while (1)
13: {
14: /*Liest die Anwendereingabe ein und verzweigt auf Basis der Eingabe.*/
15:
16: switch(menue())
17: {
18: case 1:
19: {
20: puts("\nAufgabe A wird ausgeführt.");
21: sleep(1);
22: break;
23: }
24: case 2:
25: {
26: puts("\nAufgabe B wird ausgeführt.");
27: sleep(1);
28: break;
29: }
30: case 3:
31: {
32: puts("\nAufgabe C wird ausgeführt.");
33: sleep(1);
34: break;
35: }
36: case 4:
37: {
38: puts("\nAufgabe D wird ausgeführt.");
39: sleep(1);
40: break;
41: }
42: case 5: /* Ende des Programms. */
43: {
44: puts("\nSie verlassen das Programm...\n");
45: sleep(1);
46: exit(0);
47: }
48: default:
49: {
50: puts("\nUngültige Option, versuchen Sie es noch einmal.");
51: sleep(1);
52: }
53: } /* Ende der switch-Anweisung */
54: } /* Ende der while-Schleife */
55: return 0;
56: }
57:
58: /* Gibt ein Menü aus und liest die Auswahl des Anwenders ein. */
59: int menue(void)
60: {
61: int antwort;
62:
63: puts("\nGeben Sie 1 für Aufgabe A ein.");
64: puts("Geben Sie 2 für Aufgabe B ein.");
65: puts("Geben Sie 3 für Aufgabe C ein.");
66: puts("Geben Sie 4 für Aufgabe D ein.");
67: puts("Geben Sie 5 zum Verlassen des Programms ein.");
68:
69: scanf("%d", &antwort);
70:
71: return antwort;
72: }
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
1
Aufgabe A wird ausgeführt.
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
6
Ungültige Option, versuchen Sie es noch einmal.
Geben Sie 1 für Aufgabe A ein.
Geben Sie 2 für Aufgabe B ein.
Geben Sie 3 für Aufgabe C ein.
Geben Sie 4 für Aufgabe D ein.
Geben Sie 5 zum Verlassen des Programms ein.
5
Sie verlassen das Programm...
In diesem Programm taucht eine neue Anweisung auf: der Aufruf der
Bibliotheksfunktion exit()
(Zeile 46), die in der case 5
-Klausel zum Beenden des
Programms verwendet wird. Sie können hier nicht wie in Listing 12.4 break
verwenden. Mit break
würden Sie lediglich die switch
-Anweisung verlassen, aber nicht
die umgebende while
-Schleife. Im nächsten Abschnitt gehen wir ausführlicher auf die
exit()
-Funktion ein.
Manchmal kann es nützlich sein, die Ausführung durch mehrere case
-Klauseln einer
switch
-Konstruktion »hindurchrutschen« zu lassen. Angenommen Sie wollten für
mehrere mögliche Werte des Kontrollausdrucks den gleichen Anweisungsblock
ausführen. Lassen Sie einfach die break
-Anweisungen fort und listen Sie die case
-
Marken vor den Anweisungen auf. Wenn der Kontrollausdruck mit einer der case
-
Konstanten übereinstimmt, »rutscht« die Ausführung durch die folgenden case
-
Anweisungen, bis sie auf den Code-Block trifft, der ausgeführt werden soll. Ein
Beispiel dafür finden Sie in Listing 12.8.
Listing 12.8: Eine weitere Möglichkeit für den Einsatz einer switch-Anweisung.
1: /* Eine weitere Möglichkeit für den Einsatz einer switch-Anweisung. */
2:
3: #include <stdio.h>
4: #include <stdlib.h>
5:
6: int main(void)
7: {
8: int antwort;
9:
10: while (1)
11: {
12: puts("\nGeben Sie einen Wert zwischen 1 und 10 ein, 0 für Ende:");
13: scanf("%d", &antwort);
14:
15: switch (antwort)
16: {
17: case 0:
18: exit(0);
19: case 1:
20: case 2:
21: case 3:
22: case 4:
23: case 5:
24: {
25: puts("Ihre Eingabe lautete 5 oder kleiner.\n");
26: break;
27: }
28: case 6:
29: case 7:
30: case 8:
31: case 9:
32: case 10:
33: {
34: puts("Ihre Eingabe lautete 6 oder größer.\n");
35: break;
36: }
37: default:
38: puts("Zwischen 1 und 10 bitte!\n");
39: } /* Ende der switch-Anweisung */
40: } /* Ende der while-Schleife */
41: return 0;
42: }
Geben Sie einen Wert zwischen 1 und 10 ein, 0 für Ende:
11
Zwischen 1 und 10 bitte!
Geben Sie einen Wert zwischen 1 und 10 ein, 0 für Ende:
1
Ihre Eingabe lautete 5 oder kleiner.
Geben Sie einen Wert zwischen 1 und 10 ein, 0 für Ende:
6
Ihre Eingabe lautete 6 oder größer.
Geben Sie einen Wert zwischen 1 und 10 ein, 0 für Ende:
0
Dieses Programm liest einen Wert von der Tastatur ein und teilt Ihnen dann mit, ob
der Wert bei 5
oder kleiner, bei 6
oder größer oder überhaupt nicht zwischen 1
und 10
liegt. Wenn der Wert 0
beträgt, wird in Zeile 18 ein Aufruf an die exit()
-Funktion
ausgeführt und das Programm damit beendet.
switch (Ausdruck)
{
case Konstante_1: Anweisung(en);
case Konstante_2: Anweisung(en);
...
case Konstante_n: Anweisung(en);
default: Anweisung(en);
}
Mit der switch
-Anweisung können Sie in Abhängigkeit vom Wert eines Ausdrucks in
mehrere Anweisungsblöcke verzweigen. Diese Vorgehensweise ist effizienter und
leichter nachzuvollziehen als eine tiefverschachtelte if
-Anweisung. Eine switch
-
Anweisung wertet einen Ausdruck aus und verzweigt dann zu der case
-Anweisung,
deren Konstante mit dem Ergebnis des Ausdrucks übereinstimmt. Gibt es keine
übereinstimmende case
-Marke, springt die Programmsteuerung zu der
Auffanganweisung default
. Wenn keine default
-Anweisung vorgesehen ist, springt
die Programmsteuerung an das Ende der switch
-Anweisung.
Der Programmfluss bewegt sich von der case
-Anweisung kontinuierlich nach unten,
es sei denn, eine break
-Anweisung zwingt das Programm, an das Ende der switch
-
Anweisung zu springen.
switch( buchstabe )
{
case 'A':
case 'a':
printf( "Ihre Eingabe lautete A" );
break;
case 'B':
case 'b':
printf( "Ihre Eingabe lautete B");
break;
...
...
default:
printf( "Ich habe keine case-Anweisung für %c", buchstabe );
}
switch( zahl )
{
case 0: puts( "Ihre Zahl ist 0 oder kleiner.");
case 1: puts( "Ihre Zahl ist 1 oder kleiner.");
case 2: puts( "Ihre Zahl ist 2 oder kleiner.");
case 3: puts( "Ihre Zahl ist 3 oder kleiner.");
...
...
case 99: puts( "Ihre Zahl ist 99 oder kleiner.");
break;
default: puts( "Ihre Zahl ist größer als 99.");
}
Da es in diesem Beispiel für die ersten case
-Anweisungen keine break
-Anweisungen
gibt, werden hier nach dem Sprung zu der übereinstimmenden case
-Marke alle
nachfolgenden Ausgaben ausgeführt, bis die Programmausführung auf das break
der
case 99
-Klausel stößt. Wenn die Zahl beispielsweise 3 wäre, würde das Programm
Ihnen mitteilen, dass Ihre Zahl gleich 3 oder kleiner, 4 oder kleiner, 5 oder kleiner bis
hin zu 99 oder kleiner ist. Das Programm fährt mit der Ausgabe fort, bis es auf die
break
-Anweisung in case 99
trifft.
Ein C-Programm ist normalerweise zu Ende, wenn die Ausführung auf die schließende
geschweifte Klammer der main()
-Funktion trifft. Sie können ein Programm jedoch
jederzeit durch Aufruf der Bibliotheksfunktion exit()
beenden. Sie können auch eine
oder mehrere Funktionen angeben, die bei Programmende automatisch ausgeführt
werden sollen.
Die Funktion exit()
beendet die Programmausführung und gibt die Steuerung zurück
an das Betriebssystem. Diese Funktion übernimmt ein einziges Argument vom Typ
int
, das sie an das Betriebssystem weiterreicht. Über dieses Argument kann man
anzeigen, ob das Programm erfolgreich war oder nicht. Die Syntax der exit()
-
Funktion lautet:
exit(status);
Wenn status
den Wert 0
hat, ist das ein Zeichen dafür, dass das Programm normal
beendet wurde. Jeder andere Wert ungleich 0
bedeutet, dass das Programm mit
irgendeinem Fehler beendet wurde. Dies kann sinnvoll sein, wenn Ihr C-Programm
von einem anderen Programm ausgeführt wird, beispielsweise mit Hilfe der system()
-
Funktion, die wir im nächsten Abschnitt behandeln werden.
Um die Funktion exit()
verwenden zu können, muss die Header-Datei stdlib.h
eingebunden werden. Diese Header-Datei definiert zusätzlich zwei symbolische
Konstanten, die als Argumente für die exit()
-Funktion verwendet werden können:
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
Um das Programm mit einem Rückgabewert von 0
zu verlassen, müssen Sie
exit(EXIT_SUCCESS)
aufrufen, für einen Rückgabewert von 1
exit(EXIT_FAILURE)
.
Die C-Standardbibliothek enthält eine Funktion namens system()
, mit der Sie Befehle
des Betriebssystems in einem laufenden C-Programm ausführen können. Dies kann
nützlich sein, wenn Sie das Verzeichnis einer Platte lesen oder eine Diskette
formatieren wollen. Um die Funktion system()
zu verwenden, müssen Sie die Header-
Datei stdlib.h
in das Programm mit einbinden. Das Format von system()
lautet:
system(befehl);
Das Argument befehl
kann entweder eine String-Konstante oder ein Zeiger auf einen
String sein. Um beispielsweise unter Linux den Inhalt eines Verzeichnisses auflisten zu
lassen, können Sie schreiben:
system("ls");
char *befehl = "ls";
system(befehl);
Nachdem der Betriebssystembefehl ausgeführt wurde, wird das Programm mit der
Anweisung fortgesetzt, die auf den Aufruf von system()
folgt. Wenn der Befehl, den
Sie system()
übergeben, kein gültiger Betriebssystembefehl ist, erhalten Sie die
Fehlermeldung command not found
(»Befehl nicht gefunden«). Ein Beispiel für die
system()
-Funktion finden Sie in Listing 12.9.
Listing 12.9: Mit der Funktion system() Systembefehle ausführen.
1: /* Beispiel für den Einsatz der system()-Funktion. */
2: #include <stdio.h>
3: #include <stdlib.h>
4:
5: int main(void)
6: {
7: /* Deklariert einen Puffer für die Aufnahme der Eingabe. */
8:
9: char eingabe[40];
10:
11: while (1)
12: {
13: /* Liest den Befehl des Anwenders ein. */
14:
15: puts("\nSystembefehl eingeben (Eingabetaste für Ende)");
16: fgets(eingabe, 40, stdin);
17:
18: /* Ende, wenn eine Leerzeile eingegeben wurde. */
19:
20: if (eingabe[0] == '\n')
21: exit(0);
22:
23: /* Führt den Befehl aus. */
24:
25: system(eingabe);
26: }
27: return 0;
28: }
Systembefehl eingeben (Eingabetaste für Ende):
ls *.c
list1201.c list1203.c list1205.c list1207.c list1209.c
list1202.c list1204.c list1206.c list1208.c
Systembefehl eingeben (Eingabetaste für Ende):
Listing 12.9 veranschaulicht, wie man system()
verwenden kann. Die while
-Schleife
in den Zeilen 11 bis 26 ermöglicht dem Programm die Ausführung von
Betriebssystembefehlen. Die Zeilen 15 und 16 fordern den Anwender auf, einen
Betriebssystembefehl einzugeben. Wenn der Anwender die Eingabetaste betätigt,
ohne einen Befehl einzugeben, wird die Funktion exit()
aufgerufen, die das
Programm beendet (Zeilen 20 und 21). Zeile 25 ruft system()
mit dem vom
Anwender eingegebenen Befehl auf. Wenn Sie das Programm auf Ihrem System
laufen lassen, wird Ihre Ausgabe selbstverständlich etwas anders aussehen.
Die Befehle, die Sie system()
übergeben können, sind nicht nur auf einfache
Betriebssystembefehle, wie Verzeichnisse ausgeben oder Platten formatieren,
beschränkt. Sie können genauso gut den Namen einer x-beliebigen ausführbaren
Datei oder Batch-Datei übergeben - und das Programm wird ganz normal ausgeführt.
Wenn Sie zum Beispiel das Argument list1208
übergeben, würden Sie damit das
Programm list1208 ausführen. Nach Beendigung dieses Programms kehrt die
Ausführung dorthin zurück, von wo der system()
-Aufruf erfolgt ist.
Die einzigen Beschränkungen, die es bei der Verwendung von system()
gibt, betreffen
den Speicher. Wenn system()
ausgeführt wird, bleibt das ursprüngliche Programm im
RAM und das Betriebssystem versucht, eine neue Kopie der Befehls-Shell (auf Linux-
Systemen normalerweise bash) oder das Programm, das Sie ausführen, in den
Arbeitsspeicher zu laden. Verständlicherweise ist dies nur möglich, wenn Ihr
Computer über genug Speicherkapazität verfügt. Wenn nicht, erhalten Sie eine
Fehlermeldung.
Unser heutiges Kapitel behandelte eine Reihe von Themen, die mit der
Programmsteuerung zu tun hatten. Sie haben die goto
-Anweisung kennen gelernt und
wissen jetzt, warum Sie sie in Ihren Programmen besser vermeiden sollten. Es wurden
Ihnen die break
- und die continue
-Anweisung vorgestellt, mit denen Sie größeren
Einfluss auf die Ausführung von Schleifen nehmen können. In Verbindung mit
Endlosschleifen können diese Anweisungen schwierige Programmierprobleme lösen.
Außerdem haben Sie gesehen, wie Sie mit der exit()
-Funktion ein Programm
beenden können. Abschließend wurde Ihnen gezeigt, wie Sie mit der system()
-
Funktion Systembefehle aus Ihrem Programm heraus ausführen können.
Frage:
Was ist besser, eine switch
-Anweisung oder eine verschachtelte if
-Anweisung?
Antwort:
Wenn Sie eine Variable prüfen, die mehr als zwei Werte annehmen kann, ist
die switch
-Anweisung fast immer die bessere Alternative. Der resultierende
Code ist einfacher zu lesen. Wenn Sie eine wahr/falsch
-Bedingung testen, ist
die if
-Anweisung vorzuziehen.
Frage:
Warum sollte man goto
-Anweisungen vermeiden?
Antwort:
Auf den ersten Blick scheint es, als ob die goto
-Anweisung eine recht
nützliche Anweisung sei. goto
kann jedoch mehr Probleme verursachen, als es
behebt. Eine goto
-Anweisung ist ein unstrukturierter Befehl, der Sie zu einem
anderen Punkt in einem Programm springen lässt. Viele Debugger (Software,
die Ihnen hilft, Probleme aufzuspüren) können goto
-Anweisungen nicht
ordentlich zurückverfolgen. Außerdem führen goto
-Anweisungen zu
Spaghetti-Code - Code ohne strukturieren Programmfluss.
Frage:
Ist es ratsam, mit der system()
-Funktion Systemfunktionen auszuführen?
Antwort:
Die Funktion system()
scheint auf den ersten Blick gut dafür geeignet, so
etwas wie Dateien in einem Verzeichnis auszugeben. Aber Sie sollten
vorsichtig damit sein. Die meisten Befehle eines Betriebssystems sind
betriebssystemspezifisch. Wenn Sie diese Befehle zusammen mit einem
Aufruf von system()
verwenden, ist Ihr Code unter Umständen nicht mehr
portierbar. Wenn Sie ein anderes Programm (statt eines
Betriebssystembefehls) ausführen wollen, dürften Sie keine Schwierigkeiten
mit der Portierung haben.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, sowie Übungen, die Sie anregen sollen, das Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Die Lösungen zu den Fragen und den Übungen finden Sie in Anhang C.
goto
-Anweisung zu verwenden?
break
- und einer continue
-
Anweisung?
switch
-Anweisung ausgewertet werden?
default
-Anweisung?
exit()
-Funktion?
system()
-Funktion?
switch( antwort )
{
case 'J': printf("Ihre Antwort lautete Ja");
break;
case 'N': printf( "Ihre Antwort lautete Nein");
}
switch( option )
{
default:
printf("Sie haben weder 1 noch 2 gewählt");
case 1:
printf("Ihre Antwort lautete 1");
break;
case 2:
printf( "Ihre Antwort lautete 2");
break;
}
switch
-Anweisung aus Übung 5 mit if
-Anweisungen nach.
do...while
.
system()
-
Funktion verschiedene Systembefehle ausführen.