Warum #define TRUE (1 == 1) in einem C-Booleschen Makro anstatt einfach als 1?

160

Ich habe Definitionen in C gesehen

#define TRUE (1==1)
#define FALSE (!TRUE)

Ist das notwendig? Was ist der Vorteil gegenüber der einfachen Definition von TRUE als 1 und FALSE als 0?

Robert Harvey
quelle
35
Und mehr : #define TRUE (’/’/’/’); #define FALSE (’-’-’-’)(entnommen aus coding-guidelines.com/cbook/cbook1_1.pdf Seite 871)
osgx
2
Nein, es ist Paranoia durch die Ahnungslosen. Wunderinformiert, wirklich. In C machen 1 und 0 unter allen Umständen dasselbe.
Jens
@osgx Was bedeutet das?
Mrgloom

Antworten:

155

Bei diesem Ansatz wird der tatsächliche booleanTyp verwendet (und in trueund aufgelöst false), wenn der Compiler dies unterstützt. (speziell C ++)

Es ist jedoch besser zu überprüfen, ob C ++ verwendet wird (über das __cplusplusMakro) und tatsächlich trueund zu verwenden false.

In einem C-Compiler entspricht dies 0und 1.
(Beachten Sie, dass das Entfernen der Klammern dies aufgrund der Reihenfolge der Operationen unterbricht.)

SLaks
quelle
7
Das ist falsch, Bools werden hier nicht verwendet. Das Ergebnis von 1==1ist ein int. (Siehe stackoverflow.com/questions/7687403/… .)
Mat
4
@Mat: Auch in C ++, mit dem booleanTyp?
SLaks
9
Die Frage ist mit C markiert, aber in C ++ geben die Vergleichsoperatoren trueoder zurück false.
Mat
5
@ Mat: Ich vermute, dass solcher Code in C-Headern geschrieben ist, um gut mit C ++ zu spielen
SLaks
20
@SLaks Wenn es gut mit C ++ spielen wollte, würde es #define TRUE trueund #define FALSE falsewann immer __cpluspluses definiert ist.
Nikos C.
137

Die Antwort ist Portabilität. Die numerischen Werte von TRUEund FALSEsind nicht wichtig. Was ist wichtig, dass eine Aussage wie if (1 < 2)auswertet zu if (TRUE)und eine Erklärung wie if (1 > 2)auswertet zu if (FALSE).

Zugegeben, in C wird (1 < 2)ausgewertet 1und (1 > 2)ausgewertet. 0Wie andere bereits gesagt haben, gibt es für den Compiler keinen praktischen Unterschied. Aber indem man die Compiler definieren TRUEund FALSEnach seinen eigenen Regeln, Sie machen ihre Bedeutung für Programmierer explizit, und Sie garantieren Konsistenz in Ihrem Programm und andere Bibliothek (die andere Bibliothek unter der Annahme folgt Standards C ... du würdest sei erstaunt).


Einige Geschichte
Einige BASICs definiert FALSEals 0und TRUEals -1. Wie viele moderne Sprachen interpretierten sie jeden Wert ungleich Null als TRUE, aber sie bewerteten boolesche Ausdrücke, die wahr waren als -1. Ihre NOTOperation wurde implementiert, indem 1 hinzugefügt und das Zeichen umgedreht wurde, da dies effizient war. So wurde 'NICHT x' -(x+1). Ein Nebeneffekt davon ist, dass ein Wert wie5 bewertet TRUE, aber NOT 5bewertet -6, was auch ist TRUE! Es macht keinen Spaß, diese Art von Fehler zu finden.

Best Practices
Angesichts der De-facto- Regeln, als die Null interpretiert wird FALSEund jeder Wert ungleich Null als interpretiert wird TRUE, sollten Sie niemals boolesch aussehende Ausdrücke mit TRUEoder vergleichenFALSE . Beispiele:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

Warum? Weil viele Programmierer die Abkürzung verwenden, ints als bools zu behandeln. Sie sind nicht gleich, aber Compiler erlauben es im Allgemeinen. So ist es zum Beispiel völlig legal zu schreiben

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

Das sieht legitim aus und der Compiler wird es gerne akzeptieren, aber es macht wahrscheinlich nicht das, was Sie wollen. Das liegt daran, dass der Rückgabewert von strcmp()ist

      0 if yourString == myString
    <0 if yourString < myString
    > 0 ifyourString > myString

Die obige Zeile kehrt also TRUEnur zurück, wenn yourString > myString.

Der richtige Weg, dies zu tun, ist entweder

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

oder

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

Ähnlich:

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

Einige dieser "schlechten Beispiele" finden Sie häufig im Produktionscode, und viele erfahrene Programmierer schwören darauf: Sie funktionieren, einige sind kürzer als ihre (pedantisch?) Richtigen Alternativen, und die Redewendungen sind fast allgemein anerkannt. Aber bedenken Sie: Die "richtigen" Versionen sind nicht weniger effizient, sie sind garantiert portabel, sie bestehen selbst die strengsten Linters und selbst neue Programmierer werden sie verstehen.

Ist das das nicht wert?

Adam Liss
quelle
6
(1==1)ist nicht tragbarer als 1. Die eigenen Regeln des Compilers sind die der C-Sprache, die klar und eindeutig in Bezug auf die Semantik von Gleichheits- und Vergleichsoperatoren ist. Ich habe noch nie einen Compiler gesehen, der dieses Zeug falsch verstanden hat.
Keith Thompson
1
Tatsächlich strcmpist bekannt, dass der von zurückgegebene Wert kleiner als, gleich oder größer als 0 ist. Es ist nicht garantiert, dass er -1, 0 oder 1 ist, und es gibt Plattformen in freier Wildbahn, die diese Werte nicht zurückgeben, um die Implementierungsgeschwindigkeit zu erhöhen. Also wenn strcmp(a, b) == TRUEdann a > baber umgekehrte Implikation möglicherweise nicht gilt.
Maciej Piechotka
2
@KeithThompson - Vielleicht war "Portabilität" der falsche Begriff. Es bleibt jedoch die Tatsache, dass (1 == 1) ein boolescher Wert ist; 1 ist nicht.
Adam Liss
2
@AdamLiss: In C, (1==1)und 1sind beide konstanten Ausdrücke des Typs intmit dem Wert 1. Sie semantisch identisch sind. Ich nehme an, Sie können Code schreiben, der sich an Leser richtet, die das nicht wissen, aber wo endet er?
Keith Thompson
2
'nicht' 5 ist in der Tat -6 auf Bitebene.
Woliveirajr
51

Der (1 == 1)Trick ist nützlich zum DefinierenTRUE auf eine Weise , die für C transparent ist und dennoch eine bessere Eingabe in C ++ ermöglicht. Der gleiche Code kann als C oder C ++ interpretiert werden, wenn Sie in einem Dialekt namens "Clean C" schreiben (der entweder als C oder C ++ kompiliert wird) oder wenn Sie API-Header-Dateien schreiben, die von C- oder C ++ - Programmierern verwendet werden können.

Hat in C-Übersetzungseinheiten 1 == 1genau die gleiche Bedeutung wie 1; und 1 == 0hat die gleiche Bedeutung wie 0. Hat jedoch in den C ++ - Übersetzungseinheiten den 1 == 1Typ bool. Das so TRUEdefinierte Makro lässt sich also besser in C ++ integrieren.

Ein Beispiel für eine bessere Integration ist beispielsweise, wenn die Funktion fooüberlastet istint und für hat bool, foo(TRUE)die boolÜberlastung ausgewählt wird. Wenn TRUEes nur als definiert ist 1, funktioniert es in C ++ nicht gut. foo(TRUE)will das wollenint Überlastung.

Natürlich eingeführt C99 bool, trueund falsediese können in Header - Dateien verwendet werden , dass die Arbeit mit C99 und mit C.

Jedoch:

  • diese Praxis des Definierens TRUE und FALSEals (0==0)und (1==0)vor C99.
  • Es gibt immer noch gute Gründe, sich von C99 fernzuhalten und mit C90 zu arbeiten.

Wenn Sie in einem gemischten C- und C ++ - Projekt arbeiten und C99 nicht möchten, definieren Sie die Kleinbuchstaben true.false und boolstattdessen.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

Davon abgesehen, die 0==0 abgesehen wurde (wird?) Trick von einigen Programmierern sogar in Code verwendet, der niemals dazu gedacht war, in irgendeiner Weise mit C ++ zusammenzuarbeiten. Das kauft nichts und deutet darauf hin, dass der Programmierer ein Missverständnis darüber hat, wie Boolesche Werte in C funktionieren.


Falls die C ++ - Erklärung nicht klar war, finden Sie hier ein Testprogramm:

#include <cstdio>

void foo(bool x)
{
   std::puts("bool");  
}

void foo(int x)
{
   std::puts("int");  
}

int main()
{
   foo(1 == 1);
   foo(1);
   return 0;
}

Die Ausgabe:

bool
int

Zur Frage aus den Kommentaren, wie überladene C ++ - Funktionen für die gemischte C- und C ++ - Programmierung relevant sind. Diese veranschaulichen nur einen Typunterschied. Ein gültiger Grund dafür, dass eine trueKonstante boolbeim Kompilieren als C ++ verwendet werden soll, ist die saubere Diagnose. Bei den höchsten Warnstufen warnt uns ein C ++ - Compiler möglicherweise vor einer Konvertierung, wenn wir eine Ganzzahl als boolParameter übergeben. Ein Grund für das Schreiben in Clean C ist nicht nur, dass unser Code portabler ist (da er von C ++ - Compilern verstanden wird, nicht nur von C-Compilern), sondern wir können auch von den diagnostischen Meinungen von C ++ - Compilern profitieren.

Kaz
quelle
3
Ausgezeichnete und unterschätzte Antwort. Es ist überhaupt nicht offensichtlich, dass sich die beiden Definitionen von TRUEunter C ++ unterscheiden.
user4815162342
4
Wie sind überladene Funktionen für Code relevant, der sowohl als C als auch als C ++ kompiliert wird?
Keith Thompson
@KeithThompson Es geht nicht nur um Überladung, sondern um das richtige Tippen im Allgemeinen. Überladen ist nur das praktischste Beispiel, wenn es ins Spiel kommt. Natürlich interessiert sich C ++ - Code ohne Überladungen, Vorlagen und all das "komplizierte" Zeug, das aus Gründen der "C-Kompatibilität" entfernt wurde , nicht wirklich für Typen, aber das bedeutet nicht, dass man die konzeptionellen Typbeschränkungen in einer bestimmten Sprache aufheben sollte .
Christian Rau
1
@ChristianRau: Was meinst du mit "interessiert sich nicht wirklich für Typen"? Typen spielen in der C-Sprache eine zentrale Rolle. Jeder Ausdruck, Wert und jedes Objekt in einem C-Programm hat einen genau definierten Typ. Wenn Sie in C und C ++ etwas anderes definieren möchten (in den seltenen Fällen, in denen Sie tatsächlich Code schreiben müssen, der sowohl als C als auch als C ++ kompiliert wird), können Sie #ifdef __cplusplusIhre Absicht viel klarer ausdrücken.
Keith Thompson
@ KeithThompson Ja, ich weiß, wie wichtig Typen sind. Es ist nur so, dass ohne all die typbewussten Dinge wie Überladung und Vorlagen Dinge wie die Unterscheidung zwischen boolund intin der Praxis keine große Rolle spielen, da sie implizit ineinander konvertierbar sind (und in C tatsächlich "gleich" die Anführungszeichen beachten obwohl,) und es gibt nicht viele Situationen , in denen Sie wirklich brauchen disambuigate zwischen den beiden. "nicht viel" war wahrscheinlich zu schwer, "viel weniger im Vergleich zu Code mit Vorlagen und Überladung" wäre vielleicht besser gewesen.
Christian Rau
18
#define TRUE (1==1)
#define FALSE (!TRUE)

ist äquivalent zu

#define TRUE  1
#define FALSE 0

in C.

Das Ergebnis der Vergleichsoperatoren ist 0oder 1. 1==1wird garantiert bewertet 1und !(1==1)wird garantiert bewertet0 .

Es gibt absolut keinen Grund, das erste Formular zu verwenden. Beachten Sie, dass das erste Formular jedoch nicht weniger effizient ist, da bei fast allen Compilern ein konstanter Ausdruck eher zur Kompilierungszeit als zur Laufzeit ausgewertet wird. Dies ist nach dieser Regel zulässig:

(C99, 6.6p2) "Ein konstanter Ausdruck kann während der Übersetzung und nicht zur Laufzeit ausgewertet werden und kann dementsprechend an jeder Stelle verwendet werden, an der sich eine Konstante befindet."

PC-Lint gibt sogar eine Nachricht aus (506, Boolescher Wert mit konstantem Wert), wenn Sie kein Literal für TRUEund FALSEMakros verwenden:

Für C, TRUE sollte definiert werden, zu sein 1. Andere Sprachen verwenden jedoch andere Mengen als 1, sodass einige Programmierer das Gefühl haben, auf !0Nummer sicher zu gehen.

Auch in C99 verwenden die stdbool.hDefinitionen für Boolesche Makros trueund false direkt Literale:

#define true   1
#define false  0
ouah
quelle
1
Ich habe Zweifel, TRUE wird bei jeder Verwendung durch 1 == 1 ersetzt, während nur 1 1 ersetzt. Ist dies nicht die erste Methode, die zusätzlichen Vergleichsaufwand verursacht? Oder wird der Compiler optimiert?
Pinkpanther
4
Konstante Ausdrücke von @pinkpanther werden normalerweise zur Kompilierungszeit ausgewertet und verursachen daher keinen Overhead.
Ouah
2
1==1wird garantiert bis1
ouah
3
@ NikosC. Das ist eine gute Frage. Dies ist wichtig für den Code des Formulars if(foo == true), der von einer schlechten Praxis zu einem fehlerhaften Buggy wird.
Djechlin
1
+1 für den Hinweis auf die Gefahren in (x == TRUE)kann einen anderen Wahrheitswert haben als x.
Joshua Taylor
12

Neben C ++ (bereits erwähnt) sind statische Analysewerkzeuge ein weiterer Vorteil. Der Compiler beseitigt Ineffizienzen, aber ein statischer Analysator kann seine eigenen abstrakten Typen verwenden, um zwischen Vergleichsergebnissen und anderen ganzzahligen Typen zu unterscheiden. Daher weiß er implizit, dass TRUE das Ergebnis eines Vergleichs sein muss und nicht als kompatibel angenommen werden sollte mit einer ganzen Zahl.

Offensichtlich sagt C, dass sie kompatibel sind, aber Sie können sich dafür entscheiden, die absichtliche Verwendung dieser Funktion zu verbieten, um Fehler hervorzuheben - zum Beispiel, wenn jemand verwirrt hat &und &&oder sie ihre Operator-Priorität verpfuscht haben.

sh1
quelle
1
Das ist ein guter Punkt, und vielleicht können einige dieser Tools sogar albernen Code if (boolean_var == TRUE) durch Erweiterung abfangen, auf if (boolean_var == (1 == 1))den dank der erweiterten Typinformationen des (1 == 1)Knotens in das Muster fällt if (<*> == <boolean_expr>).
Kaz
4

Der praktische Unterschied ist keiner. 0wird ausgewertet falseund 1ausgewertet true. Die Tatsache, dass Sie einen booleschen Ausdruck ( 1 == 1) oder 1zum Definieren verwenden true, macht keinen Unterschied. Sie werden beide ausgewertetint .

Beachten Sie, dass die C-Standardbibliothek einen bestimmten Header zum Definieren von Booleschen Werten enthält : stdbool.h.

Schuh
quelle
Natürlich hast du nicht ... aber einige Leute denken vielleicht anders, besonders für negative Zahlen, deshalb :)
pinkpanther
Was? Du hast es rückwärts. truewird ausgewertet 1und falseausgewertet 0. C weiß nichts über native boolesche Typen, sie sind nur Ints.
Djechlin
In C liefern relationale und Gleichheitsoperatoren Ergebnisse vom Typ intmit dem Wert 0oder 1. C hat einen tatsächlichen booleschen Typ ( _Boolmit einem in booldefinierten Makro <stdbool.h>, das jedoch nur in C99 hinzugefügt wurde, wodurch die Semantik der Operatoren für die Verwendung des neuen Typs nicht geändert wurde.
Keith Thompson
@djechlin: Ab 1999 Standard, C hat eine native boolean Typ. Es heißt _Boolund <stdbool.h>hat #define bool _Bool.
Keith Thompson
@KeithThompson, Sie haben Recht mit der 1 == 1Bewertung als int. Bearbeitet.
Schuh
3

Wir kennen den genauen Wert, dem TRUE entspricht, nicht und die Compiler können ihre eigenen Definitionen haben. Was Sie also privilegieren, ist die Verwendung des internen Compilers für die Definition. Dies ist nicht immer erforderlich, wenn Sie gute Programmiergewohnheiten haben, kann jedoch Probleme für einen schlechten Codierungsstil vermeiden, zum Beispiel:

if ((a> b) == TRUE)

Dies kann eine Katastrophe sein, wenn Sie TRUE manuell als 1 definieren, während der interne Wert von TRUE ein anderer ist.

Capiggue
quelle
In C >liefert der Operator immer 1 für wahr, 0 für falsch. Es gibt keine Möglichkeit, dass ein C-Compiler dies falsch macht. Gleichheitsvergleiche TRUEund FALSEschlechter Stil; das obige ist klarer geschrieben als if (a > b). Aber die Idee, dass verschiedene C-Compiler Wahrheit und Falsch unterschiedlich behandeln können, ist einfach falsch.
Keith Thompson
2
  1. Listenpunkt

In der Programmiersprache C wird normalerweise 1 als wahr und 0 als falsch definiert. Deshalb sehen Sie ziemlich oft Folgendes:

#define TRUE 1 
#define FALSE 0

Jede Zahl ungleich 0 würde jedoch auch in einer bedingten Anweisung als wahr bewertet. Verwenden Sie daher Folgendes:

#define TRUE (1==1)
#define FALSE (!TRUE)

Sie können nur explizit zeigen, dass Sie versuchen, auf Nummer sicher zu gehen, indem Sie false gleich dem machen, was nicht wahr ist.

Sabashan Ragavan
quelle
4
Ich würde es nicht "auf Nummer sicher gehen" nennen, sondern Sie geben sich ein falsches Gefühl der Sicherheit.
Dodgethesteamroller