vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 1

Tag 3

Anweisungen, Ausdrücke und Operatoren

C-Programme bestehen aus Anweisungen, und die meisten Anweisungen setzen sich aus Ausdrücken und Operatoren zusammen. Deshalb benötigen Sie zum Schreiben eines C-Programms eine gute Kenntnis von Anweisungen, Ausdrücken und Operatoren. Heute lernen Sie:

Anweisungen

Eine Anweisung ist ein vollständiger Befehl, der den Computer dazu anhält, eine bestimmte Aufgabe auszuführen. Normalerweise nehmen Anweisungen in C eine ganze Zeile ein. Es gibt jedoch auch einige Anweisungen, die sich über mehrere Zeilen erstrecken. C-Anweisungen werden immer mit einem Semikolon abgeschlossen (eine Ausnahme dazu bilden die Präprozessor-Direktiven #define und #include, die wir am Tag 20, »Compiler für Fortgeschrittene«, noch eingehender untersuchen werden). Einige C-Anweisungen haben Sie bereits kennen gelernt. So ist zum Beispiel

x = 2 + 3;

eine Zuweisung. Sie weist den Computer an, die Werte 3 und 2 zu addieren und das Ergebnis der Variablen x zuzuweisen. Im Laufe dieses Buches werden ich Ihnen noch weitere Formen von Anweisungen vorstellen.

Whitespace in Anweisungen

Der Begriff Whitespace bezieht sich auf Leerzeichen, Tabulatoren und leere Zeilen in Ihrem Quelltext. Dieser Whitespace wird vom C-Compiler nicht berücksichtigt. Konkret bedeutet dies, dass Ihr Compiler, wenn er eine Anweisung in Ihrem Quelltext liest, Ausschau nach den Zeichen in der Anweisung und dem abschließenden Semikolon hält und Whitespace ignoriert. Demzufolge ist die Anweisung

x=2+3;

äquivalent zu

x = 2 + 3;

aber auch äquivalent zu

x        =
2
+
3 ;

Dadurch sind Sie sehr flexibel, was die Formatierung Ihres Quellcodes angeht. Sie sollten jedoch von einer Formatierung wie im letzten Beispiel absehen. Anweisungen sollten immer jeweils eine Zeile einnehmen und links und rechts von Variablen und Operatoren die gleichen Abstände aufweisen. Wenn Sie sich an die Formatierungs- konventionen dieses Buches halten, können Sie nichts falsch machen. Mit zunehmender Erfahrung werden Sie vielleicht feststellen, dass Sie einiges doch lieber anders schreiben würden, aber das Hauptziel sollte immer ein lesbarer Quellcode sein.

Die Regel, dass C Whitespace ignoriert, hat natürlich auch eine Ausnahme. Innerhalb von literalen Stringkonstanten werden Tabulatoren und Leerzeichen nicht ignoriert, sondern als Teil des Strings interpretiert. Ein String ist eine Folge von Zeichen. Literale Stringkonstanten sind Strings, die in Anführungszeichen stehen und (Leer- )Zeichen für (Leer-)Zeichen vom Compiler gelesen werden. Ein Beispiel für einen literalen String ist

"Wadde hadde dudde da"

Dieser literale String unterscheidet sich von:

"Wadde   hadde   dudde   da"

Der Unterschied liegt in den zusätzlichen Leerzeichen. In literalen Strings werden Whitespace-Zeichen berücksichtigt.

Der folgende Code ist zwar extrem schlechter Stil, aber völlig legal in C:

printf(
"Hallo, Welt!"
);

Das folgende Beispiel wird zwar vom GNU-C-Compiler zugelassen, entspricht aber nicht den Standardregeln für C. Das bedeutet, dass Sie solchen Code vermeiden sollten, auch wenn der gcc keine Fehlermeldung ausgibt. Andernfalls könnten Sie Schwierigkeiten bekommen, wenn Sie versuchen, Ihren Code mit einem anderen C-Compiler zu kompilieren.

printf("Hallo,
Welt!");

Um eine literale Stringkonstante zu umbrechen, müssen Sie direkt vor dem Umbruch das Backslash-Zeichen (\) einfügen. Folgendes Beispiel ist demnach zulässig und kann mit allen ANSI-C-Compilern kompiliert werden.

printf("Hallo,\
Welt!");

Leeranweisungen erzeugen

Wenn Sie ein vereinzeltes Semikolon allein in eine Zeile setzen, erzeugen Sie eine so genannte Leeranweisung - eine Anweisung, die keine Aufgabe ausführt. Dies ist in C absolut zulässig. Weiter hinten in diesem Buch werden Sie erfahren, in welcher Hinsicht Leeranweisungen nützlich sein können.

Verbundanweisungen

Eine Verbundanweisung, auch Block genannt, ist eine Gruppe von zwei oder mehr C-Anweisungen, die in geschweiften Klammern steht. Sehen Sie dazu folgendes Beispiel für einen Block:

{
printf("Hallo, ");
printf("Welt!");
}

In C kann ein Block überall dort verwendet werden, wo auch eine einfache Anweisung eingesetzt werden kann. Sie werden in diesem Buch viele Beispiele dafür finden. Beachten Sie, dass die Stellung der geschweiften Klammern auch eine andere sein kann. Folgender Code ist demnach zum obigen Beispiel äquivalent:

{printf("Hallo, ");
printf("Welt!");}

Es ist ratsam, geschweifte Klammern jeweils allein in eigene Zeilen zu schreiben und damit den Anfang und das Ende eines Blocks deutlich sichtbar zu machen. Außerdem können Sie auf diese Art und Weise schneller feststellen, ob Sie eine Klammer vergessen haben.

Was Sie tun sollten

Was nicht

Gewöhnen Sie sich eine einheitliche Verwendung von Whitespace-Zeichen in Ihren Anweisungen an.

Setzen Sie die geschweiften Klammern für Blöcke jeweils in eigene Zeilen. Dadurch wird der Code leichter zu lesen.

Richten Sie die geschweiften Klammern für Blöcke untereinander aus, damit Sie Anfang und Ende eines Blocks leichter identifizieren können.

Vermeiden Sie es, einfache Anweisungen über mehrere Zeilen zu schreiben, wenn dafür kein Grund besteht. Setzen Sie, wenn möglich, die Anweisungen jeweils in eine eigene Zeile.

Ausdrücke

In C versteht man unter einem Ausdruck alles, was einen numerischen Wert zum Ergebnis hat. C-Ausdrücke können einfach oder sehr komplex sein.

Einfache Ausdrücke

Der einfachste C-Ausdruck besteht aus einem einzigen Element: einer einfachen Variablen, einer literalen Konstante oder einer symbolischen Konstante. Hier einige Beispiele für Ausdrücke:

Ausdruck

Beschreibung

PI

Eine symbolische Konstante (im Programm definiert)

20

Eine literale Konstante

rate

Eine Variable

-1.25

Noch eine literale Konstante

Eine literale Konstante wird zu ihrem Wert ausgewertet. Eine symbolische Konstante wird zu dem Wert ausgewertet, der ihr bei ihrer Erzeugung mit der #define-Direktive zugewiesen wurde. Der Wert einer Variablen ist der aktuell durch das Programm zugewiesene Wert.

Komplexe Ausdrücke

Komplexe Ausdrücke sind im Grunde genommen nur einfache Ausdrücke, die durch Operatoren verbunden sind. So ist zum Beispiel

2 + 8

ein Ausdruck, der aus den zwei Unterausdrücken 2 und 8 und dem Additionsoperator + besteht. Der Ausdruck 2 + 8 wird, wie Sie wissen, zu 10 ausgewertet. Sie können in C auch sehr komplexe Ausdrücke schreiben:

1.25 / 8 + 5 * rate + rate * rate / kosten

Wenn ein Ausdruck mehrere Operatoren enthält, wird die Auswertung des Ausdrucks von der Rangfolge der Operatoren bestimmt. Die Rangfolge der Operatoren sowie Einzelheiten zu den Operatoren in C selbst werden später in diesem Kapitel noch besprochen.

C-Ausdrücke können sogar noch interessanter sein. Betrachten Sie einmal folgende Zuweisung:

x = a + 10;

Diese Anweisung wertet den Ausdruck a + 10 aus und weist das Ergebnis x zu. Darüber hinaus ist die ganze Anweisung x = a + 10 als solche ein Ausdruck, der zu dem Wert der Variablen links des Gleichheitszeichens ausgewertet wird. Sie sehen in Abbildung 3.1 eine bildhafte Darstellung.

Abbildung 3.1:  Eine Zuweisung ist als solche auch ein Ausdruck.

Deshalb können Sie auch folgende Anweisungen schreiben, die den Wert des Ausdrucks a + 10 sowohl der Variablen x als auch der Variablen y zuweist:

y = x = a + 10;

Sie können aber auch Anweisungen wie die folgende schreiben:

x = 6 + (y = 4 + 5);

Dieser Anweisung zufolge erhält y den Wert 9 und x den Wert 15. Beachten Sie die Klammern, die erforderlich sind, damit die Anweisung kompiliert werden kann. Auf die Klammern wird weiter hinten im Kapitel noch eingegangen.

Operatoren

Ein Operator ist ein Symbol, das C anweist, eine Operation oder Aktion auf einem oder mehreren Operanden auszuführen. Ein Operand ist das, was vom Operator verarbeitet wird. In C sind alle Operanden Ausdrücke. Die C-Operatoren lassen sich in mehrere Kategorien aufteilen:

Der Zuweisungsoperator

Der Zuweisungsoperator ist das Gleichheitszeichen (=). Seine Verwendung in der Programmierung unterscheidet sich von der, die Ihnen aus der normalen Mathematik her bekannt ist. Wenn Sie in einem C-Programm

x = y;

schreiben, ist damit nicht »x ist gleich y« gemeint. Hier bedeutet das Gleichheitszeichen: »Weise den Wert von y der Variablen x zu.« In einer C-Zuweisung kann die rechte Seite ein beliebiger Ausdruck sein, die linke Seite muss jedoch ein Variablenname sein. Die korrekte Syntax lautet demzufolge:

variable = ausdruck;

Bei der Ausführung wird ausdruck ausgewertet und der daraus resultierende Wert wird variable zugewiesen.

Mathematische Operatoren

Die mathematischen Operatoren in C führen mathematische Operationen wie Addition und Subtraktion aus. C verfügt über zwei unäre und fünf binäre mathematische Operatoren.

Unäre mathematische Operatoren

Die unären mathematischen Operatoren leiten ihren Namen von der Tatsache her, dass sie nur einen Operanden benötigen. In C gibt es zwei unäre mathematische Operatoren, die in Tabelle 3.1 aufgelistet sind.

Operator

Symbol

Aktion

Beispiele

Inkrement

++

Inkrementiert den Operanden um eins

++x, x++

Dekrement

--

Dekrementiert den Operanden um eins

--x, x--

Tabelle 3.1: Unäre mathematische Operatoren in C.

Die Inkrement- und Dekrementoperatoren können nur mit Variablen und nicht mit Konstanten verwendet werden. Bei der Operation wird der Operand um eins erhöht beziehungsweise erniedrigt. Mit anderen Worten, die Anweisungen

++x;
--y;

sind äquivalent zu:

x = x + 1;
y = y - 1;

Sie sollten der Tabelle 3.1 bereits entnommen haben, dass beide unären Operatoren sowohl vor dem Operand (Präfix-Modus) als auch nach dem Operand (Postfix- Modus) gesetzt werden können. Diese beiden Modi sind nicht identisch. Der Unterschied liegt im Zeitpunkt der Inkrementierung beziehungsweise Dekrementierung:

Lassen Sie uns dies anhand eines Beispiels veranschaulichen. Betrachten wir die beiden folgenden Anweisungen:

x = 10;
y = x++;

Nach Ausführung dieser Anweisungen hat x den Wert 11 und y den Wert 10. Der Wert von x wurde erst y zugewiesen und dann inkrementiert. Im Gegensatz dazu führen die folgenden Anweisungen dazu, dass y und x beide den Wert 11 haben, da x zuerst inkrementiert und erst dann y zugewiesen wird.

x = 10;
y = ++x;

Denken Sie daran, dass = der Zuweisungsoperator ist und keine »ist-gleich«- Anweisung. Als Gedächtnisstütze können Sie sich das =-Zeichen als »Fotokopier«- Operator vorstellen. Die Anweisung y = x bedeutet: »Kopiere x nach y.« Nachfolgende Änderungen an x - nachdem die Kopie erstellt wurde - haben keine Auswirkungen mehr auf y.

Das Programm in Listing 3.1 veranschaulicht den Unterschied zwischen dem Präfix- und dem Postfix-Modus.

Listing 3.1: Der Präfix- und der Postfix-Modus.

1:   /* Der Präfix- und der Postfix-Modus bei unären Operatoren */
2:
3: #include <stdio.h>
4:
5: int a, b;
6:
7: int main(void)
8: {
9: /* Setzt a und b gleich 5 */
10:
11: a = b = 5;
12:
13: /* Beide werden mehrfach ausgegeben und jedes Mal dekrementiert. */
14: /* Für b wird der Präfix-Modus verwendet, für a der Postfix-Modus */
15:
16: printf("\nPost Prae");
17: printf("\n%d %d", a--, --b);
18: printf("\n%d %d", a--, --b);
19: printf("\n%d %d", a--, --b);
20: printf("\n%d %d", a--, --b);
21: printf("\n%d %d\n", a--, --b);
22:
23: return 0;
24: }

Post  Prae
5 4
4 3
3 2
2 1
1 0

Dieses Programm deklariert in Zeile 5 zwei Variablen, a und b. In Zeile 11 werden die Variablen auf den Wert 5 gesetzt. Im Zuge der Ausführung der einzelnen printf()- Anweisungen (Zeilen 17 bis 21) werden a und b jeweils um eins dekrementiert. a wird erst ausgegeben und dann dekrementiert, während b erst dekrementiert und dann ausgegeben wird.

Binäre mathematische Operatoren

Die binären mathematischen Operatoren in C benötigen zwei Operanden. Eine Übersicht über die binären Operatoren, mit denen die allgemeinen Taschenrechner- Kalkulationen durchgeführt werden können, finden Sie in Tabelle 3.2.

Operator

Symbol

Aktion

Beispiel

Addition

+

Addiert zwei Operanden

x + y

Subtraktion

-

Subtrahiert den zweiten Operanden vom ersten Operanden

x - y

Multiplikation

*

Multipliziert zwei Operanden

x * y

Division

/

Dividiert den ersten Operanden durch den zweiten Operanden

x / y

Modulus

%

Gibt den Rest an, der bleibt, wenn der erste Operand durch den zweiten Operanden dividiert wurde

x % y

Tabelle 3.2: Binäre mathematische Operatoren in C.

Die ersten vier Operatoren der Tabelle 3.2 sollten Sie eigentlich kennen, so dass Sie ohne Schwierigkeiten damit rechnen können. Der fünfte Operator, Modulus, ist vielleicht nicht ganz so bekannt. Er liefert den Rest einer Division zurück. So ist zum Beispiel 11 Modulus 4 gleich 3 (das heißt 11 geteilt durch 4 ist gleich 2 mit dem Rest 3). Sehen Sie dazu im Folgenden noch einige weitere Beispiele:

100 modulus 9 gleich 1
10 modulus 5 gleich 0
40 modulus 6 gleich 4

Listing 3.2 zeigt Ihnen, wie Sie mit dem Modulus-Operator eine große Sekundenzahl in Stunden, Minuten und Sekunden umwandeln können.

Listing 3.2: Beispiel für den Modulus-Operator.

1:   /* Beispiel für den Modulus-Operator. */
2: /* Liest eine Sekundenzahl ein und konvertiert diese */
3: /* in Stunden, Minuten und Sekunden. */
4:
5: #include <stdio.h>
6:
7: /* Definition von Konstanten */
8:
9: #define SEK_PRO_MIN 60
10: #define SEK_PRO_STD 3600
11:
12: unsigned sekunden, minuten, stunden, sek_rest, min_rest;
13:
14: int main(void)
15: {
16: /* Eingabe der Sekundenzahl */
17:
18: printf("Geben Sie eine Anzahl an Sekunden ein : ");
19: scanf("%d", &sekunden);
20:
21: stunden = sekunden / SEK_PRO_STD;
22: minuten = sekunden / SEK_PRO_MIN;
23: min_rest = minuten % SEK_PRO_MIN;
24: sek_rest = sekunden % SEK_PRO_MIN;
25:
26: printf("%u Sekunden entsprechen ", sekunden);
27: printf("%u h, %u m und %u s\n", stunden, min_rest, sek_rest);
28:
29: return 0;
30: }

Geben Sie eine Anzahl an Sekunden ein : 60
60 Sekunden entsprechen 0 h, 1 m, and 0 s
Geben Sie eine Anzahl an Sekunden ein : 10000
10000 Sekunden entsprechen 2 h, 46 m, and 40 s

Das Programm in Listing 3.2 hat den gleichen Aufbau wie alle vorigen Programme. Die Zeilen 1 bis 3 teilen Ihnen anhand eines Kommentars mit, was das Programm macht. Zeile 4 ist reiner Whitespace, um das Programm lesbarer zu machen. Ebenso wie Whitespace-Zeichen in Anweisungen und Ausdrücken werden auch Leerzeilen vom Compiler ignoriert. Zeile 5 bindet die für dieses Programm notwendige Header- Datei ein. Die Zeilen 9 und 10 definieren zwei Konstanten, SEK_PRO_MIN und SEK_PRO_STD, die Ihnen das Lesen der Anweisungen im Programm erleichtern sollen. Zeile 12 deklariert alle benötigten Variablen. Manche Programmierer ziehen es vor, jede Variable auf einer eigenen Zeile zu deklarieren, anstatt sie alle in eine Zeile zu setzen. Doch dies ist, wie vieles in C, nur eine Frage des Stils. Beide Methoden sind erlaubt.

In Zeile 14 steht die Funktion main(), die den Hauptteil des Programms enthält. Um Sekunden in Stunden und Minuten umzurechnen, muss das Programm zuerst die Werte mitgeteilt bekommen, mit denen es arbeiten soll. Dazu wird in Zeile 18 mit Hilfe der printf()-Funktion eine Eingabeaufforderung auf dem Bildschirm ausgegeben. In der nachfolgenden Zeile wird die eingegebene Zahl mit Hilfe der scanf()-Funktion eingelesen. Die scanf()-Anweisung speichert die umzuwandelnde Anzahl der Sekunden in der Variablen sekunden. Mehr zu den Funktionen scanf() und printf() erfahren Sie am Tag 6, »Grundlagen der Ein- und Ausgabe«. Zeile 21 enthält einen Ausdruck, der die Zahl der Stunden ermittelt (durch Teilen der Anzahl der Sekunden durch die Konstante SEK_PRO_STD). Da stunden eine Integer-Variable ist, wird der Restwert ignoriert. Zeile 22 verwendet die gleiche Logik, um die Gesamtzahl der Minuten für die eingegebene Sekundenzahl festzustellen. Da die in Zeile 22 errechnete Gesamtzahl der Minuten auch die Minuten für die Stunden enthält, verwendet Zeile 23 den Modulus-Operator, um die Gesamtzahl der Minuten durch die Anzahl an Minuten pro Stunde (entspricht dem Wert von SEK_PRO_MIN) zu teilen und die restlichen Minuten zu erhalten. Zeile 24 führt eine ähnliche Berechnung zur Ermittlung der übrig gebliebenen Sekunden durch. Die Zeilen 26 und 27 dürften Ihnen inzwischen schon bekannt vorkommen, sie übernehmen die in den Ausdrücken errechneten Werte und geben sie aus. Zeile 29 beendet das Programm mit dem Zurückgeben von 0 an das Betriebssystem.

Klammern und die Rangfolge der Operatoren

Wenn ein Ausdruck mehr als einen Operator enthält, stellt sich die Frage, in welcher Reihenfolge die Operationen ausgeführt werden. Wie wichtig diese Frage ist, zeigt die folgende Zuweisung:

x = 4 + 5 * 3;

Wenn zuerst addiert wird, erhalten Sie als Ergebnis für x den Wert 27:

x = 9 * 3;

Wird hingegen zuerst multipliziert, ergibt sich Folgendes, und x wird der Wert 19 zugewiesen.

x = 4 + 15;

Dies belegt deutlich, dass es fester Regeln bedarf, die die Reihenfolge für die Auswertung der Operationen bestimmen. Diese Reihenfolge, auch Operator- Rangfolge genannt, ist in C streng geregelt. Jeder Operator hat eine bestimmte Priorität. Wenn ein Ausdruck ausgewertet wird, werden die Operatoren mit der höheren Priorität zuerst ausgeführt. Tabelle 3.3 zeigt Ihnen die Rangfolge der mathematischen Operatoren in C. Die Zahl 1 bedeutet höchste Priorität, und Operatoren dieser Priorität werden folglich zuerst ausgeführt.

Operatoren

Relative Priorität

++ --

1

* / %

2

+ -

3

Tabelle 3.3: Rangfolge der mathematischen Operatoren in C.

Ein Blick auf die Tabelle 3.3 zeigt Ihnen, dass in allen C-Ausdrücken für die Ausführung von Operationen die folgende Reihenfolge gilt:

Wenn ein Ausdruck mehr als einen Operator der gleichen Priorität enthält, werden die Operatoren in der Regel nach ihrem Erscheinen von links nach rechts ausgeführt. So haben zum Beispiel in dem folgenden Ausdruck die Operatoren % und * die gleiche Priorität, aber % steht am weitesten links und wird deshalb auch zuerst ausgeführt.

12 % 5 * 2

Die Auswertung dieses Ausdrucks ergibt 4 (12 % 5 ergibt 2; 2 mal 2 ist 4).

Doch kehren wir zu dem Beispiel von oben zurück. Nach der hier beschriebenen Operator-Rangfolge weist die Anweisung x = 4 + 5 * 3; der Variablen x den Wert 19 zu, da die Multiplikation vor der Addition erfolgt.

Was aber, wenn Sie bei der Berechnung Ihres Ausdrucks von der Rangfolge der Operatoren abweichen wollen? Wenn Sie zum Beispiel in unserem obigen Beispiel erst 4 und 5 addieren und dann die Summe mit 3 multiplizieren wollen? In C können Sie mit Klammern auf die Auswertung des Ausdrucks beziehungsweise die Operator- Rangfolge Einfluss nehmen. Ein in Klammern gefasster Unterausdruck wird immer zuerst ausgewertet, unabhängig von der Rangfolge der Operatoren. So könnten Sie zum Beispiel schreiben:

x = (4 + 5) * 3;

Der in Klammern gefasste Ausdruck 4 + 5 wird zuerst ausgewertet, so dass x in diesem Fall der Wert 27 zugewiesen wird.

Sie können in einem Ausdruck mehrfach ineinander verschachtelte Klammern verwenden. Bei verschachtelten Klammern werden die Klammern immer von innen nach außen ausgewertet. Betrachten wir folgenden komplexen Ausdruck:

x = 25 - (2 * (10 + (8 / 2)));

Dieser Ausdruck wird wie folgt ausgewertet:

  1. Der innerste Ausdruck, 8 / 2, wird zuerst ausgewertet und ergibt den Wert 4:
    25 - (2 * (10 + 4))
  2. Eine Klammer weiter nach außen wird als nächster Ausdruck 10 + 4 ausgewertet, mit dem Ergebnis 14:
    25 - (2 * 14)
  3. Anschließend wird die letzte Klammer, 2 * 14, ausgewertet und ergibt den Wert 28:
    25 - 28
  4. Auf der Basis dieses Wertes wird der letzte Ausdruck, 25 - 28, ausgewertet und der Variablen x der Wert -3 zugewiesen:
    x = -3

Klammern müssen nicht unbedingt dazu verwendet werden, um Einfluss auf die Operator-Rangfolge zu nehmen. Sie können auch der Verdeutlichung der Bezüge dienen. Klammern müssen immer paarweise auftreten. Andernfalls gibt der Compiler eine Fehlermeldung aus.

Reihenfolge der Auswertung von Unterausdrücken

Wie bereits im vorherigen Abschnitt erläutert, werden mehrere Operatoren der gleichen Priorität in einem C-Ausdruck immer von links nach rechts ausgewertet. So wird zum Beispiel in dem Ausdruck

w * x / y * z

zuerst w mit x multipliziert, das Ergebnis der Multiplikation danach durch y geteilt und das Ergebnis der Division dann mit z multipliziert.

Über alle Prioritätsebenen hinweg gibt es jedoch keine Garantie für eine Ausführung von links nach rechts. Sehen Sie dazu folgendes Beispiel:

w * x / y + z / y

Aufgrund der Priorität werden die Multiplikation und die Division vor der Addition ausgeführt. In C gibt es jedoch keine Vorgabe, ob der Unterausdruck w * x / y oder z / y zuerst ausgewertet werden soll. Wenn Ihnen nicht ganz klar ist, warum dies von Bedeutung sein kann, betrachten Sie einmal folgendes Beispiel:

w * x / ++y + z / y

Wenn der linke Unterausdruck zuerst ausgewertet wird, wird y bei der Auswertung des zweiten Ausdrucks inkrementiert. Wird der rechte Ausdruck zuerst ausgewertet, wird y nicht inkrementiert und das Ergebnis ist ein anderes. Aus diesem Grunde sollten Sie diese Art von nichteindeutigen Ausdrücken in Ihren Programmen vermeiden.

Gegen Ende der heutigen Lektion werden im Abschnitt »Übersicht der Operator- Rangfolge« die Prioritäten aller C-Operatoren aufgelistet.

Was Sie tun sollten

Was nicht

Verwenden Sie Klammern, um klarzumachen, in welcher Reihenfolge die Operatoren in Ihrem Ausdruck ausgewertet werden sollen.

Überfrachten Sie einen Ausdruck nicht. Oft ist es sinnvoller, einen Ausdruck in zwei oder mehr Anweisungen aufzuspalten. Dies gilt vor allem für die unären Operatoren (--) und (++).

Vergleichsoperatoren

Die Vergleichsoperatoren in C dienen dazu, Ausdrücke zu vergleichen. Dabei ergeben sich Fragen wie »Ist x größer als 100?« oder »Ist y gleich 0?« Ein Ausdruck mit einem Vergleichsoperator wird mit einem Integer-Wert von 1 oder 0 ausgewertet. Dabei kann man den Wert 1 als wahr und den Wert 0 als unwahr betrachten. In Tabelle 3.4 sind die sechs Vergleichsoperatoren von C aufgeführt.

Tabelle 3.5 enthält einige Anwendungsbeispiele für Vergleichsoperatoren. Diese Beispiele verwenden literale Konstanten. Das gleiche Prinzip lässt sich aber auch auf Variablen anwenden.

Vergleichende (relationale) Anweisungen werden immer zu 0 oder 1 ausgewertet. Jede Integer-Variable, deren Wert ungleich Null ist, wird als wahr betrachtet. Nur Integer-Werte von Null sind unwahr.

Operator

Symbol

Frage

Beispiel

Gleich

==

Ist Operand 1 gleich Operand 2?

x == y

Größer als

>

Ist Operand 1 größer als Operand 2?

x > y

Kleiner als

<

Ist Operand 1 kleiner als Operand 2?

x < y

Größer gleich

>=

Ist Operand 1 größer als oder gleich Operand 2?

x >= y

Kleiner gleich

<=

Ist Operand 1 kleiner als oder gleich Operand 2?

x <= y

Nicht gleich

!=

Ist Operand 1 nicht gleich Operand 2?

x != y

Tabelle 3.4: Die Vergleichsoperatoren von C.

Ausdruck

wird gelesen als

und ausgewertet als

5 == 1

Ist 5 gleich 1?

0 (falsch)

5 > 1

Ist 5 größer als 1?

1 (wahr)

5 != 1

Ist 5 nicht gleich 1?

1 (wahr)

(5 + 10) == (3 * 5)

Ist (5 + 10) gleich (3 * 5)?

1 (wahr)

Tabelle 3.5: Anwendungsbeispiele für Vergleichsoperatoren.

Was Sie tun sollten

Was nicht

Machen Sie sich klar, was in C unter wahr und falsch verstanden wird. Bei Vergleichsoperatoren ist falsch gleichbedeutend mit 0 und jeder Wert ungleich 0 gleichbedeutend mit wahr.

Verwechseln Sie den Vergleichsoperator (==) nicht mit dem Zuweisungsoperator (=). Dies ist einer der häufigsten Fehler, der von C-Pro-grammierern gemacht wird.

Die if-Anweisung

Vergleichsoperatoren werden vornehmlich für relationale Ausdrücke in if- und while- Anweisungen verwendet, die Thema von Kapitel 5, »Grundlagen der Programmsteuerung«, sind. An dieser Stelle möchte ich Sie lediglich mit den Grundlagen von if-Anweisungen vertraut machen. Dieses Grundlagenwissen macht den Einsatz von Vergleichsoperatoren in Programmsteueranweisungen verständlicher.

Für alle, die Sie sich fragen, was eine Programmsteueranweisung überhaupt ist, sei angemerkt, dass Anweisungen in einem C-Programm normalerweise von oben nach unten, das heißt in der Folge ihres Erscheinens in dem Quellcode, ausgeführt werden. Eine Programmsteueranweisung nimmt auf die Reihenfolge der Programmausführung Einfluss. Programmsteueranweisungen können veranlassen, dass bestimmte Anweisungen mehrmals hintereinander oder unter bestimmten Umständen gar nicht ausgeführt werden. Die if-Anweisung ist eine dieser Programmsteueranweisungen in C. Weitere Anweisungen dieser Art, wie do und while, werden am Tag 5 besprochen.

In ihrer grundlegenden Form wertet die if-Anweisung einen Ausdruck aus und legt in Abhängigkeit vom Ergebnis dieser Auswertung fest, wo die Programmausführung fortzusetzen ist. Eine if-Anweisung hat folgende Form:

if (Ausdruck) 
Anweisung;

Wenn Ausdruck wahr ist, wird Anweisung ausgeführt. Ist Ausdruck hingegen unwahr, wird Anweisung nicht ausgeführt. In beiden Fällen verzweigt die Ausführung in den Code, der auf die if-Anweisung folgt. Das lässt die Schlussfolgerung zu, dass die Ausführung von Anweisung von dem Ergebnis von Ausdruck abhängt. Beachten Sie, dass die if-Anweisung sowohl aus der Zeile if (Ausdruck) als auch aus der Zeile Anweisung; besteht. Diese zwei Zeilen werden nicht als getrennte Anweisungen betrachtet.

Eine if-Anweisung kann die Ausführung mehrerer Anweisungen steuern, indem man einfach eine Verbundanweisung (einen Block) verwendet. Wie ich bereits zu Beginn dieses Kapitels definiert habe, versteht man unter einem Block eine Gruppe von zwei oder mehr Anweisungen innerhalb von geschweiften Klammern. Ein Block kann überall dort eingesetzt werden, wo auch eine einfache Anweisung verwendet werden kann. So könnten Sie eine if-Anweisung auch folgendermaßen schreiben:

if (Ausdruck)
{
Anweisung1;
Anweisung2;
/* hier steht weiterer Code */
Anweisungen;
}

Was Sie tun sollten

Was nicht

Rücken Sie die Anweisungen innerhalb eines Blocks ein, damit man sie leichter lesen kann. Dazu gehören auch die Anweisungen innerhalb eines Blocks in einer if-Anweisung.

Vergessen Sie nicht, dass Sie von zu viel Programmieren C-krank werden können.

Achten Sie darauf, an das Ende einer if-Anweisung kein Semikolon zu setzen. Eine if-Anweisung sollte immer mit der darauf folgenden Bedingung abschließen. Im folgenden Codefragment wird Anweisung1 stets ausgeführt, unabhängig davon, ob x gleich 2 ist oder nicht. Der Grund liegt darin, dass beide Zeilen jeweils separat ausgeführt und nicht als Einheit erkannt werden:

if( x == 2); /* hier darf kein Semikolon stehen! */

Anweisung1;

Wenn Sie selbst programmieren, werden Sie bald feststellen, dass if-Anweisungen meistens zusammen mit Vergleichsausdrücken verwendet werden, also in der Form: »Führe die folgende(n) Anweisung(en) nur aus, wenn (engl. if) die nachstehende Bedingung wahr ist.« Sehen Sie dazu ein Beispiel:

if (x > y)
y = x;

Dieser Code weist y den Wert von x nur dann zu, wenn x größer als y ist. Ist x nicht größer als y, wird keine Zuweisung vorgenommen. Listing 3.3 verdeutlicht den Einsatz von if-Anweisungen.

Listing 3.3: Beispiel für if-Anweisungen.

1:   /* Beispiel für if-Anweisungen */
2:
3: #include <stdio.h>
4:
5: int x, y;
6:
7: int main(void)
8: {
9: /* Liest zwei Werte ein, die getestet werden */
10:
11: printf("\nGeben Sie einen Integer-Wert für x ein: ");
12: scanf("%d", &x);
13: printf("\nGeben Sie einen Integer-Wert für y ein: ");
14: scanf("%d", &y);
15:
16: /* Testet die Werte und gibt das Ergebnis aus */
17:
18: if (x == y)
19: printf("x ist gleich y\n");
20:
21: if (x > y)
22: printf("x ist größer als y\n");
23:
24: if (x < y)
25: printf("x ist kleiner als y\n");
26:
27: return 0;
28: }

Geben Sie einen Integer-Wert für x ein: 100

Geben Sie einen Integer-Wert für y ein: 10
x ist größer als y
Geben Sie einen Integer-Wert für x ein: 10

Geben Sie einen Integer-Wert für y ein: 100
x ist kleiner als y
Geben Sie einen Integer-Wert für x ein: 10

Geben Sie einen Integer-Wert für y ein: 10
x ist gleich y

Das Listing enthält drei if-Anweisungen (Zeile 18 bis 25). Viele Zeilen in diesem Programm sollten Ihnen vertraut sein. Zeile 5 deklariert die zwei Variablen x und y, und die Zeilen 11 bis 14 fordern den Benutzer auf, Werte für diese Variablen einzugeben. In den Zeilen 18 bis 25 stehen if-Anweisungen, mit denen geprüft wird, ob x kleiner als, größer als oder gleich y ist. Beachten Sie Zeile 18, die mit einer if- Anweisung feststellt, ob x gleich y ist. Dabei möchte ich Sie daran erinnern, dass == der Gleichheitsoperator ist und »ist gleich« bedeutet. Sie sollten ihn nicht mit dem Zuweisungsoperator = verwechseln. Nachdem das Programm überprüft hat, ob die Variablen gleich sind, prüft es in Zeile 21, ob x größer ist als y, und in Zeile 24, ob x kleiner ist als y. Wenn bei Ihnen der Eindruck entsteht, dass diese Verfahrensweise etwas umständlich ist, haben Sie durchaus recht. Im nächsten Programm zeige ich Ihnen, wie Sie diese Aufgabe etwas effizienter lösen können. Aber erst einmal sollten Sie dieses Programm mit verschiedenen Werten für x und y ausführen und die Ergebnisse begutachten.

Es wird Ihnen aufgefallen sein, dass die Anweisungen
in der if-Bedingung eingerückt sind. Dies ist allgemein üblich,
um die Lesbarkeit zu erhöhen.

Die else-Bedingung

Eine if-Anweisung kann optional eine else-Bedingung umfassen. Die else- Bedingung wird folgendermaßen mit aufgenommen:

if (Ausdruck)
Anweisung1;
else
Anweisung2;

Wenn Ausdruck zu wahr ausgewertet wird, wird Anweisung1 ausgeführt. Wenn Ausdruck zu unwahr ausgewertet wird, fährt das Programm mit der else-Anweisung, das heißt Anweisung2, fort. Beide Anweisungen, Anweisung1 und Anweisung2, können Verbundanweisungen oder Blöcke sein.

Listing 3.4 ist eine Neufassung des Programms aus Listing 3.3 und enthält diesmal eine if-Anweisung mit einer else-Bedingung.

Listing 3.4: Eine if-Anweisung mit einer else-Bedingung.

1:   /* Beispiel für eine if-Anweisung mit einer else-Bedingung */
2:
3: #include <stdio.h>
4:
5: int x, y;
6:
7: int main(void)
8: {
9: /* Liest zwei Werte ein, die getestet werden */
10:
11: printf("\nGeben Sie einen Integer-Wert für x ein: ");
12: scanf("%d", &x);
13: printf("\nGeben Sie einen Integer-Wert für y ein: ");
14: scanf("%d", &y);
15:
16: /* Testet die Werte und gibt das Ergebnis aus. */
17:
18: if (x == y)
19: printf("x ist gleich y\n");
20: else
21: if (x > y)
22: printf("x ist größer als y\n");
23: else
24: printf("x ist kleiner als y\n");
25:
26: return 0;
27: }

Geben Sie einen Integer-Wert für x ein: 99

Geben Sie einen Integer-Wert für y ein: 8
x ist größer als y
Geben Sie einen Integer-Wert für x ein: 8

Geben Sie einen Integer-Wert für y ein: 99

x ist kleiner als y
Geben Sie einen Integer-Wert für x ein: 99
Geben Sie einen Integer-Wert für y ein: 99
x ist gleich y

Die Zeilen 18 bis 24 weichen etwas vom vorherigen Listing ab. Zeile 18 prüft immer noch, ob x gleich y ist. Wenn diese Bedingung erfüllt ist, erscheint x ist gleich y auf dem Bildschirm, wie in Listing 3.3. Dann allerdings endet das Programm und die Zeilen 20 bis 24 werden nicht ausgeführt. Zeile 21 wird nur ausgeführt, wenn x nicht gleich y ist oder wenn, um genau zu sein, der Ausdruck »x ist gleich y« unwahr ist. Wenn x ungleich y ist, prüft Zeile 21, ob x größer als y ist. Wenn ja, gibt Zeile 22 die Nachricht x ist größer als y aus. Andernfalls (engl. else) wird Zeile 24 ausgeführt.

Listing 3.4 verwendet eine verschachtelte if-Anweisung. Verschachteln bedeutet, eine oder mehrere C-Anweisungen in einer anderen C-Anweisung unterzubringen. Im Fall von Listing 3.4 ist eine if-Anweisung Teil der else-Bedingung der ersten if- Anweisung.

Die if-Anweisung

Form 1

if( Ausdruck )
Anweisung1;
Naechste_Anweisung;

Dies ist die einfachste if-Anweisung. Wenn Ausdruck wahr ist, wird Anweisung1 ausgeführt. Ist Ausdruck nicht wahr, wird Anweisung1 ignoriert.

Form 2

if( Ausdruck )
Anweisung1;
else
Anweisung2;
Naechste_Anweisung;

Dies ist die am häufigsten verwendete if-Anweisung. Wenn Ausdruck wahr ergibt, wird Anweisung1 ausgeführt. Andernfalls wird Anweisung2 ausgeführt

Form 3

if( Ausdruck1 )
Anweisung1;
else if( Ausdruck2 )
Anweisung2;
else
Anweisung3;
Naechste_Anweisung;

Das obige Beispiel ist eine verschachtelte if-Anweisung. Wenn der erste Ausdruck, Ausdruck1, wahr ist, wird Anweisung1 ausgeführt, bevor das Programm mit Naechste_Anweisung fortfährt. Ist der erste Ausdruck nicht wahr, wird der zweite Ausdruck, Ausdruck2, geprüft. Wenn der erste Ausdruck nicht wahr und der zweite wahr ist, wird Anweisung2 ausgeführt. Wenn beide Ausdrücke falsch sind, wird Anweisung3 ausgeführt. Nur eine der drei Anweisungen wird ausgeführt.

Beispiel 1

if( gehalt > 450000 )
steuer = .30;
else
steuer = .25;

Beispiel 2

if( alter < 18 )
printf("Minderjaehriger");
else if( alter < 65 )
printf("Erwachsener");
else
printf( "Senior");

Relationale Ausdrücke auswerten

Denken Sie daran, dass Ausdrücke mit Vergleichsoperatoren C-Ausdrücke sind, die per definitionem einen Integer-Wert als Ergebnis haben. Sie sind als Ergebnis entweder wahr (1) oder unwahr (0). Meistens werden solche relationalen Ausdrücke in if-Anweisungen oder anderen Bedingungskonstruktionen verwendet, aber man kann sie auch als rein numerische Werte verwenden. Sehen Sie dazu ein Beispiel.

Listing 3.5: Relationale Ausdrücke auswerten.

1:   /* Beispiel für die Auswertung relationaler Ausdrücke */
2:
3: #include <stdio.h>
4:
5: int a;
6:
7: int main(void)
8: {
9: a = (5 == 5); /* hat als Ergebnis 1 */
10: printf("\na = (5 == 5)\na = %d", a);
11:
12: a = (5 != 5); /* hat als Ergebnis 0 */
13: printf("\na = (5 != 5)\na = %d", a);
14:
15: a = (12 == 12) + (5 != 1); /* hat als Ergebnis 1 + 1 */
16: printf("\na = (12 == 12) + (5 != 1)\na = %d\n", a);
17: return 0;
18: }

a = (5 == 5)
a = 1
a = (5 != 5)
a = 0
a = (12 == 12) + (5 != 1)
a = 2

Die Ausgabe dieses Listings mag auf den ersten Blick etwas verwirrend erscheinen. Denken Sie daran, dass der häufigste Fehler bei der Verwendung der Vergleichsoperatoren darin besteht, das einfache Gleichheitszeichen (den Zuweisungsoperator) mit dem doppelten Gleichheitszeichens zu verwechseln. Der folgende Ausdruck ergibt 5 (und weist den Wert 5 der Variablen x zu):

x = 5

Dagegen ist das Ergebnis des folgenden Ausdrucks entweder 0 oder 1 (je nachdem, ob x gleich 5 ist oder nicht); der Wert von x wird nicht geändert.

x == 5

Wenn Sie also aus Versehen

if (x = 5)
printf("x ist gleich 5");

schreiben, wird die Nachricht immer ausgegeben, da der mit der if-Anweisung geprüfte Ausdruck immer wahr ist, unabhängig davon, was der ursprüngliche Wert von x war. Zum Glück können Sie angeben, dass beim Kompilieren alle Warnungen angezeigt werden sollen (mit gcc -Wall wie am Tag 1 beschrieben). Dann gibt gcc die folgende Warnung aus: »suggest parentheses around assignment used as truth value«.

Wenn Sie mit diesem Wissen Listing 3.5 erneut betrachten, werden Sie verstehen, warum a die jeweiligen Werte annimmt. In Zeile 9 ist der Wert 5 gleich 5, so dass a der Wahrheitswert 1 zugewiesen wird. In Zeile 12 ist die Anweisung »5 ist ungleich 5« falsch, so dass a der Wert 0 zugewiesen wird.

Fassen wir noch einmal zusammen: Vergleichsoperatoren werden benutzt, um relationale Ausdrücke zu erzeugen, die Fragen zu den Beziehungen zwischen den Ausdrücken stellen. Der von einem relationalen Ausdruck zurückgegebene Wert ist numerischer Art und lautet entweder 1 (für wahr) oder 0 (für unwahr).

Rangfolge der Vergleichsoperatoren

Wie die bereits besprochenen mathematischen Operatoren werden auch bei den Vergleichsoperatoren Prioritäten vergeben, die festlegen, in welcher Reihenfolge die Operatoren in einem Ausdruck mit mehreren Operatoren ausgeführt werden. Und auch hier können Sie mit Klammern darauf Einfluss nehmen, in welcher Reihenfolge die Operatoren des relationalen Ausdrucks ausgeführt werden sollen. Der Abschnitt »Übersicht der Operator-Rangfolge« gegen Ende der heutigen Lektion gibt Ihnen einen Gesamtüberblick über die Prioritäten aller C-Operatoren.

Zuerst sei gesagt, dass Vergleichsoperatoren in der Rangfolge unter den mathematischen Operatoren stehen. Wenn Sie beispielsweise nachfolgenden Code aufsetzen, wird zuerst 2 zu x addiert und anschließend das Ergebnis mit y verglichen:

if (x + 2 > y)

Dies entspricht der folgenden Zeile, die ein gutes Beispiel dafür ist, wie man mit Klammern mehr Klarheit schaffen kann:

if ((x + 2) > y)

Die Klammern um (x+2) sind zwar aus der Sicht des C-Compilers nicht erforderlich, machen aber besonders deutlich, dass die Summe von x und 2 mit y verglichen werden soll.

Auch unter den Vergleichsoperatoren gibt es eine Rangfolge, wie Tabelle 3.6 zeigt.

Operatoren

Relative Priorität

< <= > >=

1

!= ==

2

Tabelle 3.6: Die Rangfolge der Vergleichsoperatoren in C.

Wenn Sie also schreiben

x == y > z

so entspricht dies

x == (y > z)

da C zuerst den Ausdruck y > z auswertet und dann feststellt, ob dieser Wert (entweder 0 oder 1) gleich x ist. Sie werden Konstrukte dieser Art sicher selten, wenn überhaupt anwenden, aber Sie sollten sie kennen.

Was Sie nicht tun sollten

Vermeiden Sie Zuweisungen in den Ausdruck-Blöcken von if-Anweisungen. Das könnte andere Leser Ihres Codes verwirren und auf den Gedanken bringen, dass hier ein Fehler vorliegt, den sie dadurch korrigieren, dass sie die Zuweisung in eine Anweisung ändern, die auf logische Gleichheit prüft.

Vermeiden Sie die Verwendung des Operators »Nicht gleich« (!=) in if-Anweisungen mit einer else-Bedingung. Es ist fast immer klarer, in der else-Bedingung den Gleichheitsoperator (==) zu verwenden. So sollte man zum Beispiel den folgenden Code

if ( x != 5 )
Anweisung1;
else
Anweisung2;

besser schreiben als

if ( x == 5 )
Anweisung2;
else
Anweisung1;

Logische Operatoren

Manchmal werden Sie sich gezwungen sehen, mehr als eine vergleichende Frage gleichzeitig zu stellen. Zum Beispiel: »Wenn es 7:00 Uhr ist, ein Wochentag und ich keinen Urlaub habe, dann soll der Wecker läuten.« Mit den logischen Operatoren in C können Sie zwei oder mehr relationale Ausdrücke in einem einzigen Ausdruck zusammenfassen, der dann entweder wahr oder unwahr ist. Tabelle 3.7 stellt Ihnen die drei logischen Operatoren von C vor.

Operator

Symbol

Beispiel

AND

&&

ausdr1 && ausdr2

OR

||

ausdr1 || ausdr2

NOT

!

!ausdr1

Tabelle 3.7: Die logischen Operatoren von C.

Die Funktionsweise dieser logischen Operatoren ist in Tabelle 3.8 erläutert.

Ausdruck

Auswertung

(ausdr1 && ausdr2)

Nur wahr (1), wenn ausdr1 und ausdr2 wahr sind; andernfalls falsch (0)

(ausdr1 || ausdr2)

Wahr (1), wenn entweder ausdr1 oder ausdr2 wahr ist; nur falsch (0), wenn beide falsch sind

(!ausdr1)

Falsch (0), wenn ausdr1 wahr ist; wahr (1), wenn ausdr1 falsch ist

Tabelle 3.8: Anwendungsbeispiele für die logischen Operatoren von C.

Dieser Tabelle können Sie entnehmen, dass Ausdrücke, die logische Operatoren enthalten, entweder wahr oder unwahr sind, je nachdem ob ihr(e) Operand(en) vom Wert her wahr/falsch ist (sind). Tabelle 3.9 zeigt einige konkrete Code-Beispiele.

Ausdruck

Auswertung

(5 == 5) && (6 != 2)

Wahr (1), da beide Operanden wahr sind

(5 > 1) || (6 < 1)

Wahr (1), da ein Operand wahr ist

(2 == 1) && (5 == 5)

Falsch (0), da ein Operand falsch ist

!(5 == 4)

Wahr (1), da ein Operand falsch ist

Tabelle 3.9: Code-Beispiele für die logischen Operatoren von C.

Sie können auch Ausdrücke erzeugen, die mehrere logische Operatoren enthalten. Um zum Beispiel zu fragen, ob x gleich 2, 3 oder 4 ist, könnten Sie schreiben:

 (x == 2) || (x == 3) || (x == 4)

Die logischen Operatoren erlauben häufig, eine Frage auf mehr als eine Art zu stellen. Angenommen x ist eine Integer-Variable, dann gibt es für die obige Frage zwei weitere Schreibweisen:

(x > 1) && (x < 5)
(x >= 2) && (x <= 4)

Mehr zu wahren und falschen Werten

Sie haben bereits gelernt, dass relationale Ausdrücke in C 0 zurückgeben, wenn sie unwahr sind, und 1, wenn sie wahr sind. In diesem Zusammenhang sollte man sich darüber im Klaren sein, dass jeder numerische Wert als entweder wahr oder unwahr interpretiert wird, wenn er in C-Ausdrücken oder -Anweisungen verwendet wird, die einen logischen Wert (das heißt, wahr oder unwahr) erwarten. Die Regeln dafür lauten:

Sehen Sie dazu das folgende Beispiel, in dem der Wert von x ausgegeben wird:

x = 125;
if (x)
printf("%d", x);

Da x ein Wert ungleich Null ist, interpretiert die if-Anweisung den Ausdruck (x) als wahr. Dies lässt sich mit folgender Schreibweise für alle C-Ausdrücke noch weiter verallgemeinern:

 (Ausdruck)

entspricht der folgenden Schreibweise

(Ausdruck != 0)

Beide werden als wahr ausgewertet, wenn Ausdruck ungleich Null ist, und als falsch, wenn Ausdruck 0 ist. Unter Zuhilfenahme des NOT-Operators (!) können Sie auch Folgendes schreiben:

 (!Ausdruck)

Diese Anweisung entspricht

(Ausdruck == 0)

Rangfolge der Operatoren

Wie Sie vielleicht schon geraten haben, gibt es auch unter den logischen Operatoren in C eine Rangfolge, sowohl untereinander als auch zu den anderen Operatoren. Die Priorität des !-Operators entspricht der der unären mathematischen Operatoren ++ und --. Deshalb steht ! in der Rangfolge höher als alle Vergleichsoperatoren und alle binären mathematischen Operatoren.

Im Gegensatz dazu haben die Operatoren && und || eine viel niedrigere Priorität, niedriger als alle mathematischen und relationalen Operatoren, wenn auch && eine höhere Priorität hat als ||. Wie bei allen anderen C-Operatoren können Klammern auch bei den logischen Operatoren die Reihenfolge der Auswertung ändern. Betrachten Sie dazu folgendes Beispiel:

Sie wollen einen logischen Ausdruck schreiben, der drei einzelne Vergleiche vornimmt:

  1. Ist a kleiner als b?
  2. Ist a kleiner als c?
  3. Ist c kleiner als d?

Der ganze logische Ausdruck soll wahr ergeben, wenn Bedingung 3 und entweder Bedingung 1 oder 2 wahr ist. In diesem Fall könnten Sie schreiben:

a < b || a < c && c < d

Dieser Ausdruck entspricht vom Ergebnis her jedoch nicht Ihren Erwartungen. Da der &&-Operator eine höhere Priorität hat als ||, ist der Ausdruck äquivalent zu

a < b || (a < c && c < d)

und wird wahr, wenn (a < b) wahr ist, unabhängig davon, ob die Beziehungen (a < c) und (c < d) wahr sind. Deshalb müssen Sie

(a < b || a < c) && c < d

schreiben, um zu erzwingen, dass || vor dem && ausgewertet wird. Sehen Sie dazu ein Beispiel (Listing 3.6), in dem beide Schreibweisen des Ausdrucks ausgewertet werden. Die Variablen sind so gesetzt, dass bei korrekter Schreibweise der Ausdruck falsch (0) ergeben sollte.

Listing 3.6: Rangfolge der logischen Operatoren.

1:   #include <stdio.h>
2:
3: /* Initialisierung der Variablen. Beachten Sie, dass c nicht */
4: /* kleiner ist als d, eine der Bedingungen, auf die getestet wird. */
5: /* Deshalb sollte der gesamte Ausdruck falsch ergeben.*/
6:
7: int a = 5, b = 6, c = 5, d = 1;
8: int x;
9:
10: int main(void)
11: {
12: /* Auswertung des Ausdrucks ohne Klammern */
13:
14: x = a < b || a < c && c < d;
15: printf("\nOhne Klammern lautet das Ergebnis des Ausdrucks %d", x);
16:
17: /* Auswertung des Ausdrucks mit Klammern */
18:
19: x = (a < b || a < c) && c < d;
20: printf("\nMit Klammern lautet das Ergebnis des Ausdrucks %d\n", x);
21: return 0;
22: }

Ohne Klammern lautet das Ergebnis des Ausdrucks 1
Mit Klammern lautet das Ergebnis des Ausdrucks 0

Geben Sie dieses Listing ein und führen Sie es aus. Achten Sie auf die Warnung vom gcc:

list0306.c:14: warning: suggest parentheses around && within ||

In diesem Fall können Sie die Warnung ignorieren, da wir hier das Problem veranschaulichen wollen, über das sich der Compiler beschwert. Wichtiger ist jedoch, dass die beiden Werte, die für den Ausdruck ausgegeben werden, unterschiedlich sind, obwohl der einzige Unterschied zwischen ihnen darin besteht, dass die Anweisung in Zeile 19 Klammern aufweist.

Dieses Programm initialisiert in Zeile 7 vier Variablen mit Werten, die in den Vergleichen herangezogen werden. Zeile 8 deklariert x für die Speicherung und die Ausgabe der Ergebnisse. Zeile 14 und 19 verwenden die logischen Operatoren. Zeile 14 verwendet keine Klammern, so dass das Ergebnis von der Rangfolge der Operatoren abhängt. In diesem Fall entsprechen die Ergebnisse nicht Ihren Erwartungen. Zeile 19 hat diese Klammern gesetzt, um die Reihenfolge, in der die Ausdrücke ausgewertet werden, zu ändern.

Zusammengesetzte Zuweisungsoperatoren

Die zusammengesetzten Zuweisungsoperatoren in C bieten Ihnen die Möglichkeit, eine binäre mathematische Operation mit einer Zuweisung zu kombinieren. Angenommen Sie wollten zum Beispiel den Wert x um 5 erhöhen oder, anders ausgedrückt, 5 mit x addieren und das Ergebnis dann x zuweisen. Dann könnten Sie schreiben:

x = x + 5;

Unter Verwendung eines zusammengesetzten Zuweisungsoperators, den Sie sich am besten als eine verkürzte Form der Zuweisung vorstellen können, würden Sie schreiben:

x += 5;

In allgemeinerer Form lautet die Syntax für zusammengesetzte Zuweisungsoperatoren wie folgt (wobei op für einen binären Operator steht):

ausdr1 op= ausdr2

Dies entspricht der folgenden Schreibweise:

ausdr1 = ausdr1 op ausdr2;

Sie können den Zuweisungsoperator mit allen fünf mathematischen Operatoren, die wir oben besprochen haben, kombinieren. In Tabelle 3.10 finden Sie einige Beispiele.

Wenn Sie folgendes schreiben ...

... entspricht dies ...

x *= y

x = x * y

y -= z + 1

y = y - z + 1

a /= b

a = a / b

x += y / 8

x = x + y / 8

y %= 3

y = y % 3

Tabelle 3.10: Beispiele für zusammengesetzte Zuweisungsoperatoren.

Zusammengesetzte Operatoren reduzieren den Schreibaufwand. Die Vorteile zeigen sich vor allem, wenn die Variable links des Zuweisungsoperators einen langen Namen hat. Wie alle anderen Zuweisungen ist auch eine zusammengesetzte Zuweisung ein Ausdruck und hat den Wert, der der linken Seite zugewiesen wurde, als Ergebnis. Die Ausführung der folgenden Anweisungen ergibt für beide, x und z, den Wert 14:

x = 12;
z = x += 2;

Der Bedingungsoperator

Der Bedingungsoperator ist der einzige ternäre Operator in C, das heißt der einzige Operator, der drei Operanden benötigt. Seine Syntax lautet:

ausdr1 ? ausdr2 : ausdr3;

Wenn ausdr1 wahr ist (das heißt einen Wert ungleich Null hat), erhält der gesamte Ausdruck den Wert von ausdr2. Wenn ausdr1 falsch (das heißt, Null) ist, erhält der gesamte Ausdruck den Wert von ausdr3. So weist zum Beispiel die folgende Anweisung x den Wert 1 zu, wenn y wahr ist, oder den Wert 100, wenn y falsch ist:

x = y ? 1 : 100;

Dementsprechend können Sie wie folgt z mit der größeren der beiden Variablen x oder y gleichsetzen:

z = (x > y) ? x : y;

Vielleicht ist Ihnen aufgefallen, dass der Bedingungsoperator sehr stark an die if- Anweisung erinnert. Die obige Anweisung ließe sich auch wie folgt schreiben:

if (x > y)
z = x;
else
z = y;

Der Bedingungsoperator kann eine if...else-Konstruktion nicht in allen Fällen ersetzen, er ist aber wesentlich kürzer. Außerdem kann der Bedingungsoperator auch dort verwendet werden, wo eine if-Anweisung nicht möglich ist, zum Beispiel innerhalb eines Aufrufs einer anderen Funktion, etwa einer printf()-Anweisung.

printf( "Der größere Wert lautet %d", ((x > y) ? x : y) );

Der Komma-Operator

Das Komma wird in C häufig als ein einfaches Satzzeichen verwendet, das dazu dient, Variablendeklarationen, Funktionsargumente etc. voneinander zu trennen. In bestimmten Situationen jedoch fungiert das Komma als Operator und nicht nur als einfaches Trennzeichen. Sie können einen Ausdruck bilden, indem Sie zwei Unterausdrücke durch ein Komma trennen. Das Ergebnis sieht folgendermaßen aus:

Die folgende Anweisung weist x den Wert b zu, inkrementiert a und inkrementiert dann b:

x = (a++ , b++);

Da der ++-Operator im Postfix-Modus verwendet wird, wird der Wert von b der Variablen x vor seiner Inkrementierung zugewiesen. Hier sind Klammern nötig, da der Komma-Operator eine niedrige Priorität hat, sogar noch niedriger als der Zuweisungsoperator.

Wie Sie morgen sehen werden, wird der Komma-Operator am häufigsten in for- Anweisungen verwendet.

Was Sie tun sollten

Was nicht

Verwenden Sie (ausdruck == 0) anstelle von (!ausdruck). Kompiliert haben diese zwei Ausdrücke das gleiche Ergebnis, nur ist der erste lesbarer.

Verwenden Sie die logischen Operatoren && und || anstelle von verschachtelten if- Anweisungen.

Verwechseln Sie den Zuweisungsoperator (=) nicht mit dem Gleichheitsoperator (==).

Übersicht der Operator-Rangfolge

Tabelle 3.11 gibt eine Übersicht über alle C-Operatoren in der Reihenfolge ihrer absteigenden Priorität. Operatoren der gleichen Priorität stehen in einer Zeile.

Priorität

Operatoren

1

-> . () (Funktionsoperator) [] (Array-Operator)

2

! ~ ++ - * (Indirektion) & (Adressoperator)

() (Typumwandlung)

sizeof + (unär) - (unär)

3

* (Multiplikation) / %

4

+ -

5

<< >>

6

< <= > >=

7

== !=

8

& (bitweises UND)

9

^

10

|

11

&&

12

||

13

?:

14

= += -= *= /= %= &= ^= |= <<= >>=

15

,

Tabelle 3.11: Rangfolge der C-Operatoren.

Diese Tabelle eignet sich gut als Referenz, bis Sie mit der Rangfolge der Operatoren besser vertraut sind. Sie werden Sie wahrscheinlich später benötigen.

Zusammenfassung

Die heutige Lektion war sehr umfangreich. Sie haben gelernt, was eine C-Anweisung ist, dass Whitespace-Zeichen vom Compiler nicht berücksichtigt werden und dass Anweisungen immer mit einem Semikolon abschließen. Außerdem wissen Sie jetzt, dass eine Verbundanweisung (oder Block), die aus zwei oder mehr Anweisungen in geschweiften Klammern besteht, überall dort eingesetzt werden kann, wo auch eine einfache Anweisung möglich ist.

Viele Anweisungen bestehen aus einer Kombination von Ausdrücken und Operatoren. Denken Sie daran, dass man unter dem Begriff »Ausdruck« alles zusammenfasst, was einen numerischen Wert zurückliefert. Komplexe Ausdrücke können aus vielen einfacheren Ausdrücken zusammengesetzt sein, die dann Unterausdrücke genannt werden.

Operatoren sind C-Symbole, die dem Computer mitteilen, eine Operation auf einem oder mehreren Ausdrücken auszuführen. Einige Operatoren sind unär, das heißt, sie benötigen nur einen Operanden. Die meisten C-Operatoren sind jedoch binär und erfordern zwei Operanden. Ein Operator, der Bedingungsoperator, ist sogar ternär. Innerhalb der Operatoren in C gibt es eine feste Hierarchie der Prioritäten, die festlegt, in welcher Reihenfolge die Operationen in einem Ausdruck auszuführen sind, der mehrere Operatoren enthält.

Die heute besprochenen C-Operatoren lassen sich in drei Kategorien unterteilen:

Sie wurden mit den Grundlagen der if-Anweisung bekannt gemacht, die es Ihnen ermöglicht, auf Basis der Auswertung von relationalen Ausdrücken den Programmfluss zu steuern.

Fragen und Antworten

Frage:
Welche Auswirkung haben Leerzeichen und leere Zeilen auf die Ausführung Ihres Programms?

Antwort:
Whitespace-Zeichen (leere Zeilen, Leerzeichen, Tabulatoren) machen Ihren Quellcode lesbarer. Wenn das Programm kompiliert wird, werden die Whitespace-Zeichen entfernt, haben also keinen Einfluss auf das ausführbare Programm. Deshalb sollten Sie möglichst verschwenderisch mit Whitespace- Zeichen umgehen, um Ihre Programme so lesbar wie möglich zu machen.

Frage:
Ist es ratsamer, eine komplexe if-Anweisung zu formulieren oder mehrere if- Anweisungen zu verschachteln?

Antwort:
Ihr Code sollte leicht verständlich sein. Wenn Sie if-Anweisungen verschachteln, werden diese nach den oben ausgeführten Regeln ausgewertet. Wenn Sie eine einzige komplexe if-Anweisung verwenden, werden die Ausdrücke nur soweit ausgewertet, bis der gesamte Ausdruck falsch ergibt.

Frage:
Was ist der Unterschied zwischen unären und binären Operatoren?

Antwort:
Wie die Namen schon verraten, benötigen unäre Operatoren nur eine Variable, binäre Operatoren hingegen zwei.

Frage:
Ist der Subtraktionsoperator (-) binär oder unär?

Antwort:
Er ist beides! Der Compiler ist intelligent genug, um an der Anzahl der Variablen zu erkennen, welchen Operator Sie gerade meinen. In der folgenden Anweisung ist er unär:

x = -y;

wohingegen er hier binär verwendet wird:

x = a - b;

Frage:
Werden negative Zahlen als wahr oder als unwahr betrachtet?

Antwort:
Zur Erinnerung: 0 steht für unwahr und jeder andere Wert steht für wahr. Dazu gehören dann auch die negativen Zahlen.

Workshop

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.

Quiz

  1. Wie nennt man die folgende C-Anweisung und was bedeutet sie?
    x = 5 + 8;
  2. Was ist ein Ausdruck?
  3. Was bestimmt in einem Ausdruck mit mehreren Operatoren die Reihenfolge, in der die Operationen ausgeführt werden?
  4. Angenommen die Variable x hat den Wert 10. Wie lauten die Werte für x und a, nachdem jede der beiden folgenden Anweisungen getrennt ausgeführt wurde?
    a = x++;
    a = ++x;
  5. Wie lautet das Ergebnis des Ausdrucks 10 % 3?
  6. Wie lautet das Ergebnis des Ausdrucks 5 + 3 * 8 / 2 + 2?
  7. Formulieren Sie den Ausdruck in Frage 6 durch Hinzufügen von Klammern so um, dass das Ergebnis 16 lautet.
  8. Welchen Wert hat ein Ausdruck, der zu unwahr ausgewertet wird?
  9. Welche Operatoren haben in der folgenden Liste die höhere Priorität?
  10. a. == oder <
  11. b. * oder +
  12. c. != oder ==
  13. d. >= oder >
  14. Wie lauten die zusammengesetzten Zuweisungsoperatoren und inwiefern sind sie nützlich?

Übungen

  1. Der folgende Code ist nicht gerade bester Programmierstil. Geben Sie den Code ein und kompilieren Sie ihn, um festzustellen, ob er sich ausführen lässt.
    #include <stdio.h>
    int x,y;int main(void){ printf(
    "\nGeben Sie zwei Zahlen ein");scanf(
    "%d %d",&x,&y);printf(
    "\n\n%d ist größer",(x>y)?x:y);return 0;}
  2. Formulieren Sie den Code aus Übung 1 so um, dass er lesbarer wird.
  3. Ändern Sie Listing 3.1 so, dass aufwärts statt abwärts gezählt wird.
  4. Schreiben Sie eine if-Anweisung, die der Variablen y den Wert von x nur dann zuweist, wenn x zwischen 1 und 20 ist. Lassen Sie y unverändert, wenn x nicht in diesem Wertebereich liegt.
  5. Verwenden Sie für die Aufgabe aus Übung 4 den Bedingungsoperator.
  6. Formulieren Sie die folgenden verschachtelten if-Anweisungen um und verwenden Sie dazu eine einfache if-Anweisung und logische Operatoren.
    if (x < 1)
    if ( x > 10 )
    anweisung;
  7. Wie lauten die Ergebnisse der folgenden Ausdrücke?
  8. a. (1 + 2 * 3)
  9. b. 10 % 3 * 3 - (1 + 2)
  10. c. ((1 + 2) * 3)
  11. d. (5 == 5)
  12. e. (x = 5)
  13. Angenommen es seien x = 4, y = 6 und z = 2. Stellen Sie fest, ob die folgenden Ausdrücke wahr oder falsch sind.
  14. a. if( x == 4)
  15. b. if(x != y - z)
  16. c. if(z = 1)
  17. d. if(y)
  18. Schreiben Sie eine if-Anweisung, die feststellt, ob jemand juristisch gesehen ein Erwachsener (Alter 18) ist, aber noch nicht das Rentenalter erreicht hat (Alter 65).
  19. FEHLERSUCHE: Beheben Sie die Fehler im folgenden Programm, so dass es sich ausführen lässt.
    /* ein Programm mit Problemen... */
    #include <stdio.h>
    int x= 1:
    int main(void)
    {
    if( x = 1);
    printf(" x ist gleich 1" );
    andernfalls
    printf(" x ist ungleich 1");
    return 0;
    }


vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH