5.6    Logische Verknüpfungen

Für komplexere Bedingungen (und später auch Schleifen) können sogenannte logische Operatoren verwendet werden. Mit logischen Operatoren können Sie mehrere Ausdrücke miteinander in einer Bedingung verknüpfen. Dies ist beispielsweise nötig, wenn ein Code nur dann ausgeführt werden soll, wenn zwei oder mehr Bedingungen zutreffen oder wenn auch nur eine von mehreren Bedingungen zutrifft. Für solche Zwecke bietet C die logischen Operatoren UND, ODER und NICHT an. Die entsprechenden Symbole sind in Tabelle 5.2 kurz beschrieben.

Operator

Bedeutung

Beispiel

Ergebnis

&&

UND-Operator

A && B

Gibt ungleich 0 (wahr) zurück, wenn A und B ungleich 0 sind. Ansonsten wird 0 (unwahr) zurückgegeben.

||

ODER-Operator

A || B

Gibt ungleich 0 (wahr) zurück, wenn A oder B (oder beide) ungleich 0 sind. Ansonsten wird 0 (unwahr) zurückgegeben.

!

NICHT-Operator

Gibt ungleich 0 (wahr) zurück, wenn A nicht ungleich 0 ist. Ist A hingegen ungleich 0 (wahr), wird 0 zurückgegeben.

Tabelle 5.2     Logische boolesche Operatoren in C

5.6.1    Der !-Operator

Der logische NICHT-Operator (NOT) ist ein unärer Operator und wird gerne verwendet, um eine Bedingung auf einen Fehler hin zu testen. Anstatt immer zu testen, ob eine bestimmte Bedingung gleich 0 zurückgibt, wird der !-Operator verwendet. Ein Beispiel:

if(ausdruck==0) { // Fehler } 

Hier wird getestet, ob ausdruck gleich 0 ist. In der Praxis handelt es sich allerdings eher selten um eine Überprüfung, ob ein Ganzzahlwert einer Variablen gleich 0 ist, sondern ob ein bestimmter Ausdruck oder der Rückgabewert einer Funktion gleich 0 – also unwahr – und somit fehlerhaft ist. Daher finden Sie hier statt des Vergleichs mit dem ==-Operator auf 0 eher den logischen !-Operator. Die äquivalente Schreibweise mit dem logischen NICHT-Operator wäre:

if ( !ausdruck ) { // Fehler } 

Listing 5.7 zeigt hierzu ein Beispiel, das eine einfache Passworteingabe in Form einer Geheimnummer überprüft:

00  // Kapitel5/geheimnummer.c
01 #include <stdio.h>

02 int main(void) {
03 int geheimnummer = 0;
04 printf("Geheimnummer eingeben: ");
05 int check = scanf("%d", &geheimnummer);
06 if( check != 1 ) {
07 printf("Fehler bei der Eingabe\n");
08 return 1;
09 }
10 else if( ! (geheimnummer == 123456) ) {
11 printf("Geheimnummer ist falsch!\n");
12 }
13 else {
14 printf("Geheimnummer ist richtig!\n");
15 }
16 return 0;
17 }

Listing 5.7     »geheimnummer.c« implementiert eine einfache Passwortabfrage mit dem logischen »!«-Operator. Natürlich sollten Sie Passwörter niemals im Klartext speichern.

Der logische NICHT-Operator wird in Zeile (10) ausgeführt. So wird überprüft, ob die Bedingung zwischen den Klammern, nämlich ob die Variable geheimnummer dem Wert 123456 entspricht, nicht zutrifft. Allerdings hätten Sie in diesem Fall keinen Vorteil, wenn Sie statt der Zeile (10) folgenden äquivalenten Code verwenden würden:

if(geheimnummer != 123456 ) 

In der Praxis ist es tatsächlich fast immer möglich, eine Alternative für den logischen NICHT-Operator zu verwenden. Häufiger als in unserem trivialen Beispiel in geheimnummer.c wird der NICHT-Operator verwendet, um Funktionen auf eine erfolgreiche Ausführung hin zu überprüfen. Ein Beispiel wäre:

if(! funktion() ) {
// Fehler bei der Funktionsausführung
}

Logischer NICHT-Operator

Schon mit dem C99-Standard wurde als alternative Schreibweise für den logischen !-Operator das Makro not hinzugefügt, das in der Header-Datei iso646.h definiert ist. Damit unterstützen quasi alle Compiler auch die Alternative not. Somit entspricht die Schreibweise if(!a) der Schreibweise if(not a). Wenn Sie einen Compiler verwenden, der C23 unterstützt, müssen Sie iso646.h nicht einmal mehr einbinden, weil not hier wirklich als Schlüsselwort betrachtet wird. Es kann allerdings geschehen, dass iso646.h im Laufe der Zeit vollständig entfernt wird, spätestens aber mit dem nächsten Standard C26. Die Zeile (10) aus Listing 5.7 würde mit dem Makro aber folgendermaßen aussehen:

#include <iso646.h>   // benötigte Header-Datei für not
...
if( not (geheimnummer == 123456) ) {
// Geheimnummer falsch
}
...

5.6.2    Der &&-Operator – Logisches UND

Mit dem logischen UND-Operator (&&) können Sie mehrere Operanden miteinander verknüpfen. Weil Sie zweimal & hintereinanderschreiben, weiß der Compiler, dass Sie keine Bits miteinander verknüpfen wollen. Mehrere mit UND verknüpfte Anweisungen ergeben nur dann wahr – also ungleich 0 – zurück, wenn alle einzelnen Operanden wahr sind. Ansonsten gibt der Ausdruck 0 – also unwahr – zurück. Hierzu ein Beispiel:

if( (Bedingung1) && (Bedingung2) )  {
// Beide Bedingungen sind wahr
}
else {
// Mindestens eine Bedingung ist 0 (also unwahr)
}

Listing 5.8 demonstriert den logischen UND-Operator in der Praxis:

00  // Kapitel5/und_operator.c
01 #include <stdio.h>

02 int main(void) {
03 int ival = 0;
04 printf("Eine Zahl von 1 bis 10 eingeben: ");
05 int check = scanf("%d", &ival);
06 if( check != 1 ) {
07 printf("Fehler bei der Eingabe\n");
08 return 1;
09 }
10 if( (ival > 0) && (ival <=10) ) {
11 printf("Zahl ist zwischen 1 und 10\n");
12 }
13 else {
14 printf("Zahl ist nicht zwischen 1 und 10\n");
15 }
16 return 0;
17 }

Listing 5.8     »und_operator.c« demonstriert die Verwendung des »&&«-Operators.

In diesem Beispiel werden Sie aufgefordert, eine Zahl zwischen 1 und 10 einzugeben. In Zeile (10) wird dann überprüft, ob der eingegebene Wert von ival größer als 0 und kleiner oder gleich 10 ist. Trifft beides zu, ist der Ausdruck wahr und es wird die entsprechende Meldung aus Zeile (11) ausgegeben. Andernfalls wird die Meldung aus Zeile (14) ausgegeben.

Das Programm gibt bei der Ausführung z. B. Folgendes aus:

Eine Zahl von 1 bis 10 eingeben: 3
Zahl ist zwischen 1 und 10

Ein Zahl von 1 bis 10 eingeben: 0
Zahl ist nicht zwischen 1 und 10

Ein Zahl von 1 bis 10 eingeben: 10
Zahl ist zwischen 1 und 10

[»]  Abbruch bei einer logischen UND-Verknüpfung

Ist bei einer logischen UND-Verknüpfung die links stehende Bedingung gleich 0 (unwahr), wird im Gegensatz zum bitweisen UND (&) die rechts stehende Bedingung nicht mehr überprüft, weil der Gesamtausdruck bereits unwahr ist. Dieses Verhalten soll die Performance Ihres Programms verbessern und kann bei manchen Compilern auch abgeschaltet werden.

5.6.3    Der ||-Operator – Logisches ODER

Benötigen Sie hingegen eine logische Verknüpfung, bei der der gesamte Ausdruck wahr zurückgibt, wenn nur mindestens einer der verknüpften Operanden wahr ist, dann können Sie dies mit dem ODER-Operator (||) realisieren. Weil Sie zweimal | hintereinanderschreiben, weiß der Compiler, dass Sie keine Bits miteinander verknüpfen wollen. Auch hierzu folgt ein kurzes Beispiel, damit Sie den logischen ODER-Operator besser verstehen:

if( (Bedingung1) || (Bedingung2) )  {
// Mindestens Bedingung1 oder Bedingung2 ist wahr.
}
else {
// Beide Bedingungen sind unwahr.
}

Natürlich gibt es auch hierzu wieder ein Codebeispiel, um den ODER-Operator in der Praxis kennenzulernen:

00  // Kapitel5/oder_operator.c
01 #include <stdio.h>

02 int main(void) {
03 unsigned int uval1 = 0, uval2 = 0;
04 printf("Bitte zwei Ganzzahlen eingeben: ");
05 int check = scanf("%u %u", &uval1, &uval2);
06 if(check != 2) {
07 printf("Fehler bei der Eingabe...\n");
08 return 1;
09 }
10 if( (!uval1) || (!uval2) ) {
11 printf("Fehler: Einer der Werte ist gleich 0\n");
12 }
13 else {
14 printf("%u / %u = %lf\n",
uval1, uval2, (double)uval1/uval2);
15 }
16 return 0;
17 }

Listing 5.9     »oder_operator.c« demonstriert die Verwendung des »||«-Operators.

In diesem Beispiel sollen zwei Ganzzahlen uval1 und uval2 dividiert werden. Dafür werden die beiden eingegebenen Ganzzahlen in Zeile (10) mit dem logischen NICHT-Operator überprüft. Ist mindestens eine der beiden Zahlen gleich null, wird die Fehlermeldung in Zeile (11) ausgegeben und die Berechnung in Zeile (14) nicht ausgeführt.

[»]  Abbruch bei einer logischen ODER-Verknüpfung

Ist bei einer logischen Verknüpfung eine Bedingung gleich 1 (wahr), werden alle weiteren damit verknüpften Bedingungen nicht mehr überprüft, weil der Gesamtausdruck bereits wahr ist. Auch dieses Verhalten soll die Performance Ihrer Programme steigern und kann bei manchen Compilern abgeschaltet werden.

Das Programm gibt bei der Ausführung Folgendes aus:

Bitte zwei Ganzzahlen eingeben: 10 0
Fehler: Einer der Werte ist gleich 0

Bitte zwei Ganzzahlen eingeben: 10 4
10 / 4 = 2.500000

[»]  && und || miteinander mischen

Sie können natürlich auch mit dem &&-Operator und dem ||-Operator weitere Bedingungen miteinander verknüpfen. Hierbei sollten Sie aber stets die Lesbarkeit des Codes im Auge behalten und eventuell auch Klammern setzen. Schreiben Sie also lieber if ((a==0)||(b==0)) statt if (a==0||b==0).

Logische UND- und ODER-Makros

Seit C99 sind auch für die logischen Operator-Gegenstücke && und || die Makros and und or vorhanden, also unterstützt quasi jeder Compiler auch die Alternativen and und or. Sie werden (wie auch schon im Falle von not) in der Header-Datei iso646.h definiert und gelten ab C23 als eigene Schlüsselwörter.

Zeile (10) von Listing 5.9 würde mit den Makros wie folgt aussehen:

#include <iso646.h> // benötigte Header-Datei
...
if( (not uval1) or (not uval2) )