Verwenden von Booleschen Werten in C.


Antworten:


1049

Vom Besten zum Schlechten:

Option 1 (C99)

#include <stdbool.h>

Option 2

typedef enum { false, true } bool;

Option 3

typedef int bool;
enum { false, true };

Option 4

typedef int bool;
#define true 1
#define false 0

Erläuterung

  • Option 1 funktioniert nur, wenn Sie C99 verwenden, und dies ist die "Standardmethode". Wählen Sie dies wenn möglich.
  • Die Optionen 2, 3 und 4 haben in der Praxis das gleiche identische Verhalten. # 2 und # 3 verwenden jedoch keine #defines, was meiner Meinung nach besser ist.

Wenn Sie unentschlossen sind, gehen Sie mit # 1!


1
Können Sie erläutern, warum sie die besten bis schlechtesten Entscheidungen sind?
Endolith

1
@endolith Die Ausrichtung, Optimierung und Art der Speicherung eines <stdbool.h> boolvom Compiler ausgewählten Werts ist möglicherweise für den beabsichtigten Zweck eines booleschen Werts besser geeignet als die Verwendung eines int(dh der Compiler kann sich dafür entscheiden, einen boolanderen als einen zu implementieren int). Wenn Sie Glück haben, kann dies auch zu einer strengeren Typprüfung beim Kompilieren führen.
blubberdiblub

1
Warum intfür verwenden bool? Das ist verschwenderisch. Verwenden Sie unsigned char. Oder C des builtin verwenden _Bool.
Asadefa

@NoBody Die Verwendung eines kleineren Typs kann Speicherplatz sparen, macht ihn jedoch möglicherweise nicht schneller. Oft ist es schneller, die native Wortgröße des Prozessors anstelle einer kleineren Größe zu verwenden, da der Compiler möglicherweise Bitverschiebungen vornehmen muss, um sie richtig auszurichten
Ted Klein Bergman,

241

Ein paar Gedanken zu Booleschen Werten in C:

Ich bin alt genug, dass ich nur einfaches ints als meinen booleschen Typ verwende, ohne typedefs oder spezielle Definitionen oder Aufzählungen für wahre / falsche Werte. Wenn Sie meinem Vorschlag unten folgen, niemals mit booleschen Konstanten zu vergleichen, müssen Sie ohnehin nur 0/1 verwenden, um die Flags zu initialisieren. Ein solcher Ansatz kann jedoch in diesen modernen Zeiten als zu reaktionär angesehen werden. In diesem Fall sollte man auf jeden Fall verwenden, <stdbool.h>da es zumindest den Vorteil hat, standardisiert zu werden.

Unabhängig davon, wie die booleschen Konstanten heißen, verwenden Sie sie nur zur Initialisierung. Schreibe niemals so etwas

if (ready == TRUE) ...
while (empty == FALSE) ...

Diese können immer durch den Reiniger ersetzt werden

if (ready) ...
while (!empty) ...

Beachten Sie, dass diese tatsächlich vernünftigerweise und verständlich laut vorgelesen werden können.

Geben Sie Ihren booleschen Variablen positive Namen, dh fullanstelle von notfull. Letzteres führt zu schwer lesbarem Code. Vergleichen Sie

if (full) ...
if (!full) ...

mit

if (!notfull) ...
if (notfull) ...

Beide der ersteren Paare lesen natürlich, während !notfulles schwierig ist, es zu lesen, und es wird in komplexeren booleschen Ausdrücken viel schlimmer.

Boolesche Argumente sollten generell vermieden werden. Stellen Sie sich eine so definierte Funktion vor

void foo(bool option) { ... }

Innerhalb des Hauptteils der Funktion ist sehr klar, was das Argument bedeutet, da es einen bequemen und hoffentlich bedeutungsvollen Namen hat. Die Anrufseiten sehen jedoch so aus

foo(TRUE);
foo(FALSE):

Hier ist es im Wesentlichen unmöglich zu sagen, was der Parameter bedeutet, ohne immer die Funktionsdefinition oder Deklaration zu betrachten, und es wird viel schlimmer, sobald Sie noch mehr boolesche Parameter hinzufügen. Ich schlage auch vor

typedef enum { OPT_ON, OPT_OFF } foo_option;
void foo(foo_option option);

oder

#define OPT_ON true
#define OPT_OFF false
void foo(bool option) { ... }

In beiden Fällen sieht die Anrufseite jetzt so aus

foo(OPT_ON);
foo(OPT_OFF);

was der Leser zumindest eine Chance hat zu verstehen, ohne die Definition von auszubaggern foo.


1
Und wie vergleicht man zwei Variablen auf Gleichheit? Die Verwendung von Booleschen Konstanten funktioniert niemals gut, löst jedoch das Problem beim Vergleich mit einer Nichtkonstante nicht.
Baruch

Vergib mir, aber ich verstehe die Frage nicht. Fragen Sie sich, wie ich zwei boolesche Variablen auf Gleichheit vergleiche? Wenn ja, funktioniert das nicht a == b?
Dale Hagglund

5
@ Kenji Was Sie sagen, ist wahr, obwohl ich glaube, dass es fast immer eine schlechte Idee ist, andere Werte als einen als Äquivalent für wahr zu verwenden. In Ihrem Beispiel würde ich stattdessen empfehlen , dies anzunehmen aund bvon Null hochzuzählen a > 0 == b > 0. Wenn Sie darauf bestehen, die Wahrhaftigkeit beliebiger Werte ungleich Null auszunutzen, erhalten Sie !!varden booleschen Wert 0/1, der äquivalent ist var, sodass Sie schreiben können !!a == !!b, obwohl einige Leser dies verwirrend finden.
Dale Hagglund

3
!a == !bist auch ausreichend, um die Gleichheit zu testen, Nicht-Nullen werden Null und Nullen werden Eins.
Ryanpattison

5
@rpattiso Sie haben natürlich ganz recht, aber ich denke, ich würde !!aals "nicht-boolesches a in seinen äquivalenten Wahrheitswert konvertieren" lesen, während ich !aals "logisch invertieren der booleschen Variablen a" lesen würde . Insbesondere würde ich nach einem bestimmten Grund suchen, aus dem die logische Umkehrung gewünscht wurde.
Dale Hagglund


74

Hier ist die Version, die ich verwendet habe:

typedef enum { false = 0, true = !false } bool;

Da false nur einen Wert hat, ein logisches true jedoch viele Werte haben kann, setzt die Technik true so, wie es der Compiler für das Gegenteil von false verwendet.

Dies behebt das Problem, dass jemand etwas codiert, das darauf zurückzuführen wäre:

if (true == !false)

Ich denke, wir sind uns alle einig, dass dies keine gute Praxis ist, aber für die einmaligen Kosten für "true =! False" beseitigen wir dieses Problem.

[EDIT] Am Ende habe ich verwendet:

typedef enum { myfalse = 0, mytrue = !myfalse } mybool;

um eine Namenskollision mit anderen Schemata zu vermeiden, die trueund definiert wurden false. Das Konzept bleibt jedoch dasselbe.

[EDIT] So zeigen Sie die Konvertierung einer Ganzzahl in einen Booleschen Wert an:

mybool somebool;
int someint = 5;
somebool = !!someint;

Der erste (ganz rechts)! wandelt die Ganzzahl ungleich Null in eine 0 um, dann die zweite (ganz links)! wandelt die 0 in einen myfalseWert um. Ich werde es dem Leser als Übung überlassen, eine Null-Ganzzahl umzuwandeln.

[BEARBEITEN] Es ist mein Stil, die explizite Einstellung eines Werts in einer Aufzählung zu verwenden, wenn der spezifische Wert erforderlich ist, selbst wenn der Standardwert der gleiche wäre. Beispiel: Da false Null sein muss, verwende ich false = 0,eher alsfalse,


5
Auch ein weiterer Vorteil Aufzählungen zu verwenden ist die IDE - Integration - true, falseund boolsind die in den meisten IDE hervorgehoben , weil sie ENUM - Werte und ein typedef sind, im Gegensatz zu #defines, die sind selten jemals Syntax - Hervorhebungen.

Neugierig: Wenn man ignoriert, ob es tatsächlich funktioniert oder nicht, ist es C (99+) gültig, damit eine Aufzählung auf einen vorherigen Wert in derselben Aufzählung verweist ?

@ tgm1024 gcc -ansi -pedantic -Wallgibt keine Warnungen, also vertraue ich gcc; Wenn dies auch für funktioniert, c89sollte es auch funktionieren c99.
yyny

1
"Weil false nur einen Wert hat, aber ein logisches true viele Werte haben kann, aber die Technik setzt true so, wie es der Compiler für das Gegenteil von false verwendet." Der Negationsoperator !kann nur Werte zurückgeben 0und 1weist daher true = !falseimmer den Wert 1 zu. Diese Methode bietet keine zusätzliche Sicherheit typedef enum { false, true } bool;.
user694733

1
Das früheste, was ich gefunden habe, stammt aus C90 (6.3.3.3 Unäre arithmetische Operatoren): "Das Ergebnis des logischen Negationsoperators! Ist 0, wenn der Wert seines Operanden ungleich 0,1 ist. 1, wenn der Wert seines Operanden gleich 0 ist Ergebnis hat den Typ int. Der Ausdruck! E entspricht (O == E). " Dies sollte jeden Compiler abdecken, der jemals behauptet hat, Standard C zu unterstützen. Compiler können diese Regel natürlich rechtlich ignorieren, wenn dies für das beobachtbare Verhalten (wie if(!value)) nicht von Bedeutung ist, aber diese Ausnahme gilt in diesem speziellen Fall nicht.
user694733


30

Das wichtigste zuerst. C, dh ISO / IEC 9899 hat seit 19 Jahren einen booleschen Typ . Das ist viel länger als die erwartete Länge der C-Programmierkarriere mit Amateur- / akademischen / professionellen Teilen, die beim Besuch dieser Frage kombiniert werden . Meins übertrifft das nur um 1-2 Jahre. Dies bedeutet, dass während der Zeit , in der ein durchschnittlicher Leser überhaupt etwas über C gelernt hat, C tatsächlich den booleschen Datentyp hatte .

Für den Datentyp #include <stdbool.h>und Verwendung true, falseund bool. Oder ist es nicht, und die Verwendung _Bool, 1und 0stattdessen.


In den anderen Antworten auf diesen Thread werden verschiedene gefährliche Praktiken beschrieben. Ich werde sie ansprechen:

typedef int bool;
#define true 1
#define false 0

Dies ist ein Nein-Nein, da ein Gelegenheitsleser - der in diesen 19 Jahren C gelernt hat - erwarten würde, dass boolsich dies auf den tatsächlichen bool Datentyp bezieht und sich ähnlich verhält, dies jedoch nicht! Zum Beispiel

double a = ...;
bool b = a;

Mit C99 bool/ _Bool, bwürde eingestellt werden , um false iff a Null war, und truesonst. C11 6.3.1.2p1

  1. Wenn ein Skalarwert in konvertiert wird, _Boolist das Ergebnis 0, wenn der Wert gleich 0 ist. Andernfalls ist das Ergebnis 1. 59)

Fußnoten

59) NaNs sind nicht gleich 0 und wandeln sich daher in 1 um.

Mit dem typedefan Ort und Stelle, das doublewäre einen gezwungen werden int- wenn der Wert des Doppels ist nicht in dem Bereich für intdas Verhalten ist nicht definiert .

Gleiches gilt natürlich auch, wenn trueund falsein einem deklariert wurden enum.

Was noch gefährlicher ist, ist zu erklären

typedef enum bool {
    false, true
} bool;

Da jetzt alle Werte außer 1 und 0 ungültig sind und ein solcher Wert einer Variablen dieses Typs zugewiesen werden sollte, wäre das Verhalten völlig undefiniert .

Daher iff Sie nicht C99 aus unerklärlichen Gründen, für boolean Variablen verwenden können , sollten Sie verwenden:

  • Typ intund Werte 0und 1 wie sie sind ; und führen Sie sorgfältig Domain-Konvertierungen von anderen Werten zu diesen mit doppelter Negation durch!!
  • oder wenn Sie darauf bestehen , Sie sich nicht erinnern , dass 0 ist falsy und Nicht-Null Truish, zumindest Verwendung oberen Fall , so dass sie mit den C99 - Konzepte werden nicht verwirrt: BOOL, TRUEund FALSE!

1
Welcher Teil des C-Standards würde Objekte mit aufgezählten Typen darauf beschränken, die darin explizit aufgeführten Werte zu halten? Wenn der größte Wert für eine Aufzählungs Konstante kleiner als UCHAR_MAX oder USHRT_MAX ist, könnte eine Implementierung einen Typen verwenden , die kleiner als intoder unsigned inteine Aufzählung zu halten, aber ich weiß von nichts in der Norm , die eine Aufzählung verursachen würde als etwas anderes als eine ganze Zahl verhalten Art.
Supercat

18
typedef enum {
    false = 0,
    true
} t_bool;

2
2 bis MAX_INT sollte auch als wahr auswerten
Technosaurus

2
@technosaurus Dieser Ansatz garantiert nicht! false == true, da! false eine beliebige Zahl ungleich Null sein kann. Eine einfache Umgehung wäre, explizit! Falsch true zuzuweisen.
Andrew

3
@ Andrew Das stimmt nicht. !0 = 1nach dem C-Standard und !a = 0für jeden Wert ungleich Null von a. Das Problem ist, dass jede Nicht-Null als wahr angesehen wird. Also , wenn aund bbeide „wahr“ ist , ist es nicht unbedingt der Fall , dass `a == b '.
Jeremy West

14

C hat einen booleschen Typ: bool (zumindest für die letzten 10 (!) Jahre)

Fügen Sie stdbool.h hinzu und true / false funktioniert wie erwartet.


12
10 Jahre im Standard, aber nicht 10 Jahre in Compilern! Die C-Kompilierung von MSVC ++ unterstützt C99 überhaupt nicht, außer // Kommentare zuzulassen, und wird dies wahrscheinlich nie tun. Außerdem ist _Bool in C99 als integrierter Typ definiert, während bool ein typedef im Header <stdbool.h> ist.
Clifford

5
@ Clifford 4 Jahre nach Ihrem Kommentar ... nichts hat sich geändert. MSVC ist ein C ++ - Compiler, und ich glaube, MS hat gesagt, dass sie nicht wirklich daran interessiert sind, alle neuen C-Funktionen (C99 & C11) zu unterstützen. Aber ich kann nicht davon ausgehen, dass MSVC keine neuen C-Funktionen als Grund unterstützt (insbesondere, wenn Sie dies gegen eine Antwort von 10 Jahren sagen ). 10 Jahre sind wirklich eine lange Zeit in der Programmierwelt. Jeder anständige Compiler sollte in weniger als 10 Jahren Unterstützung dafür haben, wenn der Anbieter dies unterstützen soll.
PP

2
@KingsIndian: Ich bin mir nicht sicher, warum Sie Ihren Kommentar an mich gerichtet haben oder überhaupt das Bedürfnis hatten, überhaupt einen Kommentar abzugeben. Ich habe nur die Situation zum Zeitpunkt des Schreibens angegeben. Ich habe diese Situation nicht unterstützt und lediglich darauf hingewiesen, dass die "Antwort" möglicherweise nicht unter allen Umständen gilt.
Clifford

@ Clifford: Streng genommen muss der Standard boolein Makro sein, das erweitert wird _Bool. Der Unterschied ist wichtig, weil Sie #undefein Makro können (und das ist zumindest als Übergangsmaßnahme zulässig), aber Sie können kein untypedeftypedef. Es ändert jedoch nichts an der Hauptausrichtung Ihres ersten Kommentars.
Jonathan Leffler

2
VS2015 und später (& möglicherweise früher, bis zu einem Punkt) hat kein Problem mit booldurch <stdbool.h>unter C Kompilierung. Es löst sich auf _Bool.

11

Alles, was ungleich Null ist, wird in booleschen Operationen als wahr ausgewertet

#define TRUE 1
#define FALSE 0

und benutze die Konstanten.


10
Verwenden Sie sie jedoch mit Vorsicht: Da ein echtes Ergebnis ein Wert ungleich Null sein kann, sind die Tests if (t == TRUE) {...} und if (t), die in anderen Sprachen äquivalent sind, in C nicht äquivalent .
Fortega

1
Sie haben Recht, aber das gilt auch für C ++, das einen Bool-Typ hat, oder? Während des Debuggens habe ich Bool-Variablen mit Werten von 5837834939 gesehen ...
Ggambett

1
In C ++ entspricht der if (t == true) -Test dem if (t) -Test, da C ++ eine Konvertierung durchführt (alles, was nicht 0 oder ein Nullzeigerwert ist, wird in true konvertiert)
Fortega

6
Alles, was Sie über einen booleschen wahren Wert annehmen sollten, ist, dass er nicht Null ist. Code wie if (b) ist also sicher, if if (b == TRUE) nicht; Letzteres ist schlechte Praxis (und sinnlos).
Clifford

5

Nur eine Ergänzung zu anderen Antworten und einige Erläuterungen, wenn Sie C99 verwenden dürfen.

+-------+----------------+-------------------------+--------------------+
|  Name | Characteristic | Dependence in stdbool.h |        Value       |
+-------+----------------+-------------------------+--------------------+
| _Bool |   Native type  |    Don't need header    |                    |
+-------+----------------+-------------------------+--------------------+
|  bool |      Macro     |           Yes           | Translate to _Bool |
+-------+----------------+-------------------------+--------------------+
|  true |      Macro     |           Yes           |   Translate to 1   |
+-------+----------------+-------------------------+--------------------+
| false |      Macro     |           Yes           |   Translate to 0   |
+-------+----------------+-------------------------+--------------------+

Einige meiner Vorlieben:

  • _Booloder bool? Beide sind in Ordnung, sehen aber boolbesser aus als das Schlüsselwort _Bool.
  • Akzeptierte Werte für boolund _Boolsind: falseoder true. Zuweisen 0oder 1anstelle von falseoder trueist gültig, aber es ist schwieriger, den Logikfluss zu lesen und zu verstehen.

Einige Infos aus dem Standard:

  • _Boolist NICHT unsigned int, ist aber Teil der Gruppe vorzeichenloser Ganzzahltypen . Es ist groß genug, um die Werte 0oder zu halten 1.
  • NICHT, aber ja, Sie können neu definieren bool trueund falsedas ist sicher keine gute Idee. Diese Fähigkeit gilt als veraltet und wird in Zukunft entfernt.
  • Das Zuweisen eines Skalartyps (arithmetische Typen und Zeigertypen) zu _Booloder bool, wenn der Skalarwert gleich ist 0oder mit diesem verglichen 0wird, ist 0, andernfalls lautet das Ergebnis 1: _Bool x = 9; 9wird konvertiert, 1wenn es zugewiesen wird x.
  • _Boolist 1 Byte (8 Bit), normalerweise ist der Programmierer versucht, die anderen Bits zu verwenden, wird jedoch nicht empfohlen, da nur garantiert wird, dass nur ein Bit zum Speichern von Daten verwendet wird, nicht wie bei einem Typ charmit 8 Bits verfügbar.

2

Es ist das:

#define TRUE 1
#define FALSE 0

7
Ich würde mit so etwas wie #define TRUE! FALSE
Tom

2

Sie können ein Zeichen oder einen anderen Container mit kleiner Nummer dafür verwenden.

Pseudocode

#define TRUE  1
#define FALSE 0

char bValue = TRUE;

Auch in C ist es normalerweise ein int, und es kann zu einem Verlust von Präzisionswarnungen durch anderen Code führen, der int verwendet.
Thomas Bonini

Wenn Sie nicht für den Speicherplatz von Hand optimieren, ist es immer besser, die normale Wortgröße der Hardware zu verwenden (z. B. normalerweise eine int), da Sie bei einigen Architekturen einen erheblichen Leistungseinbruch erzielen, wenn Sie diese Variablen entpacken / maskieren müssen.
Kingsley

2

Sie könnten _Bool verwenden, aber der Rückgabewert muss eine Ganzzahl sein (1 für wahr, 0 für falsch). Es wird jedoch empfohlen, bool wie in C ++ einzuschließen und zu verwenden, wie in dieser Antwort aus dem daniweb-Forum sowie in dieser Antwort aus dieser anderen Stackoverflow-Frage angegeben:

_Bool: Der boolesche Typ von C99. Die direkte Verwendung von _Bool wird nur empfohlen, wenn Sie Legacy-Code verwalten, der bereits Makros für bool, true oder false definiert. Andernfalls werden diese Makros im Header standardisiert. Wenn Sie diesen Header einschließen, können Sie bool wie in C ++ verwenden.


2

Bedingte Ausdrücke gelten als wahr, wenn sie nicht Null sind. Der C-Standard verlangt jedoch, dass logische Operatoren selbst entweder 0 oder 1 zurückgeben.

@ Tom: #define TRUE! FALSE ist schlecht und völlig sinnlos. Wenn die Header-Datei in kompilierten C ++ - Code gelangt, kann dies zu Problemen führen:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Einige Compiler generieren eine Warnung über die Konvertierung von int => bool. Manchmal vermeiden die Leute dies, indem sie Folgendes tun:

foo(flag == TRUE);

um den Ausdruck als C ++ - Bool zu erzwingen. Aber wenn Sie TRUE! FALSE definieren, erhalten Sie:

foo(flag == !0);

Dies führt zu einem Int-to-Bool-Vergleich, der die Warnung trotzdem auslösen kann.


1

Wenn Sie C99 verwenden, können Sie den _BoolTyp verwenden. Es #includesind keine s erforderlich. Sie müssen es jedoch wie eine Ganzzahl behandeln, wo 1ist trueund 0ist false.

Sie können dann TRUEund definieren FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;

Oder Sie können #include <stdbool.h>und verwenden bool, trueund falsewie der Standard es wünscht.
SS Anne

1

Heutzutage unterstützt C99 boolesche Typen, aber Sie müssen #include <stdbool.h>.

Beispiel:

#include <stdbool.h>

int main() 
{ 
    bool arr[2] = {true, false}; 

    printf("%d\n", arr[0] && arr[1]);
    printf("%d\n", arr[0] || arr[1]);

    return 0; 
} 

Ausgabe:

0
1

0

Das benutze ich:

enum {false, true};
typedef _Bool bool;

_Bool ist ein eingebauter Typ in C. Er ist für boolesche Werte vorgesehen.


-2

Sie können die #defineDirektive einfach wie folgt verwenden:

#define TRUE 1
#define FALSE 0
#define NOT(arg) (arg == TRUE)? FALSE : TRUE
typedef int bool;

Und verwenden Sie wie folgt:

bool isVisible = FALSE;
bool isWorking = TRUE;
isVisible = NOT(isVisible);

und so weiter


5
Das NOT-Makro sollte durch Klammern um argund den Ausdruck als Ganzes geschützt werden : #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). Es wäre jedoch besser, auf Falschheit zu testen (es wird die richtige Antwort geben, selbst wenn arg23 statt 0 oder 1 war : #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE). Aber der gesamte Ausdruck kann #define NOT(arg) (!(arg))natürlich auf das reduziert werden , was das gleiche Ergebnis liefert.
Jonathan Leffler
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.