Menu

Booleans in C: stdbool.h, _Bool und Wahrheitswerte

C hatte bis C99 keinen Wahrheitstyp. Null ist falsch, alles andere ist wahr, Vergleiche liefern int - und stdbool.h gibt dir obendrauf bool, true und false. Dazu der = statt ==-Bug, den dieses Design ermöglicht.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

C verbrachte seine ersten 27 Jahre ohne einen Wahrheitstyp. Das ist kein Versehen - es ist eine Designentscheidung mit Konsequenzen, denen du an deinem ersten Tag begegnest, und mit einem berühmten Bug, der direkt daraus folgt.

Null ist falsch, alles andere ist wahr

In C gibt es in den Regeln für Bedingungen keinen besonderen Wahr/Falsch-Wert. if, while, for, &&, || und ! stellen alle genau eine Frage: Ist dieser Wert null?

Negative Zahlen sind wahr. Brüche sind wahr. Ein Zeiger ungleich null ist wahr. Nur die Null - in welchem Typ auch immer - ist falsch.

Deshalb ist C-Code voller Idiome, die knapp aussehen, bis man die Regel kennt:

if (count)            /* wenn count nicht null ist */
if (!count)           /* wenn count null ist */
if (p)                /* wenn p nicht NULL ist */
if (!strcmp(a, b))    /* wenn die Strings gleich sind - strcmp liefert 0 bei Übereinstimmung */

Das letzte erwischt viele. strcmp liefert 0, wenn die Zeichenketten übereinstimmen, „gleich" liest sich in einem if also als „nicht". if (strcmp(a, b) == 0) zu schreiben ist klarer und tut genau dasselbe.

Vergleiche liefern int

Ein Vergleich ist in C keine besondere Art von Ausdruck. Er wertet zu einem gewöhnlichen int mit dem Wert 1 oder 0 aus, und du kannst ihn ausgeben, speichern oder damit rechnen:

Der !-Operator funktioniert genauso: Er liefert 1, wenn sein Operand null ist, und sonst 0. !!x ist daher ein klassisches Idiom, das jeden Wert auf genau 0 oder 1 normiert.

bool, true und false (C99)

C99 fügte einen echten Wahrheitstyp namens _Bool hinzu sowie einen Header, der ihm einen lesbaren Namen gibt:

<stdbool.h> ist winzig: Er definiert bool als _Bool, true als 1 und false als 0. Das ist der ganze Header. Doch ihn zu benutzen macht die Absicht sichtbar, was int flag = 1; nie tut.

_Bool hat ein wirklich eigenes Verhalten: Er speichert nur 0 oder 1. Die Zuweisung eines Werts ungleich null wird zu genau 1 konvertiert:

Es gibt in printf weiterhin kein %b für Wahrheitswerte - gib sie mit %d aus, oder wandle sie selbst in Text um:

In C23 wurde das noch einfacher: bool, true und false wurden echte Schlüsselwörter, das Einbinden ist also nicht mehr nötig. Der meiste Code zielt heute noch auf C17, behalte also das #include <stdbool.h>.

Einen Wahrheitswert aus einer Funktion zurückgeben

Hier verdient sich bool seinen Platz am deutlichsten:

Ein bool-Rückgabetyp sagt dem Aufrufer, was der Wert bedeutet. Ein int-Rückgabetyp einer Funktion namens check_file könnte ein Wahrheitswert, eine Anzahl oder ein Fehlercode sein - und nach den Konventionen der C-Bibliothek ist es oft das Dritte, wobei 0 Erfolg bedeutet. Den Typ zu benennen nimmt das Raten heraus.

Der = statt ==-Bug

Weil die Zuweisung ein Ausdruck ist, der den zugewiesenen Wert liefert, und weil jeder Wert ungleich null wahr ist, kompiliert das hier:

Zwei Dinge gingen gleichzeitig schief. Die Bedingung wies x den Wert 5 zu und zerstörte damit seinen Wert, und testete dann 5, was ungleich null ist, also lief der Zweig. Das Programm hat standardmäßig weder einen Fehler noch eine Warnung.

Schlimmer noch: Die Variante mit 0 führt ihren Zweig nie aus:

if (found = 0) { /* wird nie genommen - und found ist jetzt 0 */ }

Drei Verteidigungslinien, nach Nützlichkeit geordnet:

Mit Warnungen kompilieren. -Wall fängt es ab:

warning: suggest parentheses around assignment used as truth value

Auf null Warnungen zu kommen ist hier die eigentliche Lösung, und sie kostet ein Flag.

Die Konstante zuerst schreiben. Die „Yoda-Bedingung" macht aus dem Tippfehler einen Übersetzungsfehler:

if (5 == x)   /* korrekt */
if (5 = x)    /* Fehler: kein L-Wert - der Compiler hält dich auf */

Manche Teams lieben das, andere finden es unleserlich; beide Positionen sind vertretbar, aber es funktioniert.

Sei bewusst, wenn du es so meinst. Eine Zuweisung in einer Bedingung ist beim Lesen von Eingaben wirklich nützlich:

int c;
while ((c = getchar()) != EOF) { ... }

Die zusätzlichen Klammern um c = getchar() sind wegen der Vorrangregeln nötig, und sie signalisieren zugleich, dass die Zuweisung beabsichtigt ist. Das ist das Idiom - wenn deine Zuweisung-in-einer-Bedingung nicht so aussieht, ist sie wahrscheinlich ein Tippfehler.

Häufige Fehler mit Wahrheitswerten

Mit true vergleichen. if (flag == true) funktioniert mit stdbool.h, geht aber in dem Moment kaputt, in dem flag ein int mit dem Wert 42 ist, denn 42 == 1 ist falsch. Schreibe if (flag).

Verkettete Vergleiche. if (0 < x < 10) ist immer wahr: 0 < x liefert 0 oder 1, und beide sind kleiner als 10. Schreibe if (x > 0 && x < 10).

Bitweise statt logisch. & und | sind nicht && und ||. Sie werten nicht abkürzend aus und sie verknüpfen Bits:

Beide Operanden sind „wahr", und dennoch ist a & b gleich 0. & zu benutzen, wo du && meintest, erzeugt eine Bedingung, die nur für bestimmte Werte falsch ist - die schwerste Art von Bug.

Gleitkommazahlen auf Gleichheit prüfen. if (0.1 + 0.2 == 0.3) ist falsch. Vergleiche stattdessen gegen eine Toleranz - siehe Datentypen für den Grund.

Häufig gestellte Fragen

Hat C einen Wahrheitstyp?

Seit C99 ja: _Bool ist ein eingebauter Typ, und das Einbinden von <stdbool.h> gibt dir die freundlicheren Schreibweisen bool, true und false. Vor C99 gab es überhaupt keinen Wahrheitstyp - Programme benutzten int mit 0 für falsch und 1 für wahr, was weiterhin funktioniert und weiterhin verbreitet ist.

Was gilt in C als wahr?

Jeder Wert ungleich null. 1, -5, 0.01 und ein Zeiger ungleich null sind in einer Bedingung alle wahr; nur 0, 0.0 und ein Nullzeiger sind falsch. Es gibt keine separate Wahrheitsprüfung - if (x) testet schlicht, ob x ungleich null ist.

Wie benutze ich bool in C?

Füge #include <stdbool.h> hinzu, dann funktioniert bool ready = true; wie erwartet. Unter der Haube ist bool gleich _Bool, das nur 0 oder 1 speichert - die Zuweisung eines beliebigen Werts ungleich null speichert genau 1.

Was ist der Unterschied zwischen = und == in C?

= weist zu, == vergleicht. if (x = 5) weist x den Wert 5 zu und testet dann 5, was ungleich null ist, also läuft der Zweig immer. if (x == 5) prüft auf Gleichheit. Das Erste kompiliert ohne Fehler - deshalb gibt es -Wall und die Angewohnheit, if (5 == x) zu schreiben.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S