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

Lesezeit: 11 Minuten

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?

  • Und mehr: #define TRUE (’/’/’/’); #define FALSE (’-’-’-’) (genommen von Coding-Guidelines.com/cbook/cbook1_1.pdf Seite 871)

    – osgx

    9. Juni 2013 um 14:03 Uhr

  • Nein, es ist Paranoia der Ahnungslosen^Wunderinformiert, wirklich. In C tun 1 und 0 unter allen Umständen dasselbe.

    – Jens

    9. Juni 2013 um 16:05 Uhr


  • @osgx Was bedeutet das?

    – Mrgloom

    9. Juli 2019 um 18:15 Uhr

Benutzeravatar von SLaks
SLaks

Dieser Ansatz verwendet das Ist boolean eingeben (und auflösen true und false), wenn der Compiler dies unterstützt. (insbesondere C++)

Es wäre jedoch besser zu prüfen, ob C++ verwendet wird (über die __cplusplus Makro) und tatsächlich verwenden true und false.

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

  • Das ist falsch, Bools werden hier nicht verwendet. Das Ergebnis von 1==1 ist ein int. (siehe stackoverflow.com/questions/7687403/… .)

    – Matte

    9. Juni 2013 um 13:34 Uhr

  • @Mat: Sogar in C++, mit der boolean Typ?

    – SLaks

    9. Juni 2013 um 13:35 Uhr

  • Die Frage ist mit C gekennzeichnet, aber in C++ kehren die Vergleichsoperatoren tatsächlich zurück true oder false.

    – Matte

    9. Juni 2013 um 13:37 Uhr


  • @Mat: Ich vermute, dass solcher Code in C-Headern geschrieben ist, um gut mit C++ zu spielen

    – SLaks

    9. Juni 2013 um 13:38 Uhr

  • @SLaks Wenn es gut mit C++ spielen wollte, würde es das tun #define TRUE true und #define FALSE false wann immer __cplusplus ist definiert.

    – Nikos C.

    9. Juni 2013 um 13:46 Uhr

Benutzeravatar von Adam Liss
Adam Liss

Die Antwort ist Portabilität. Die numerischen Werte von TRUE und FALSE sind nicht wichtig. Was ist Wichtig ist, dass eine Aussage wie if (1 < 2) wertet zu if (TRUE) und eine Aussage wie if (1 > 2) wertet zu if (FALSE).

Zugegeben, in C, (1 < 2) wertet zu 1 und (1 > 2) wertet zu 0, also gibt es, wie andere gesagt haben, keinen praktischen Unterschied, was den Compiler betrifft. Aber indem man den Compiler definieren lässt TRUE und FALSE nach ihren eigenen Regeln machen Sie ihre Bedeutung für Programmierer deutlich und garantieren die Konsistenz innerhalb Ihres Programms und jeder anderen Bibliothek (vorausgesetzt, die andere Bibliothek folgt den C-Standards … Sie wären erstaunt).


Einige Geschichten

Einige BASICs definiert FALSE wie 0 und TRUE wie -1. Wie viele moderne Sprachen, sie interpretiert jeder Wert ungleich Null als TRUEaber sie ausgewertet boolesche Ausdrücke, die als wahr waren -1. Ihr NOT Die Operation wurde durch Addieren von 1 und Umdrehen des Vorzeichens implementiert, weil es so effizient war. So wurde ‘NOT x’ -(x+1). Ein Nebeneffekt davon ist, dass ein Wert wie 5 wertet zu TRUEaber NOT 5 wertet zu -6was auch ist TRUE! Es macht keinen Spaß, solche Fehler zu finden.

Empfohlene Vorgehensweise

Angesichts der de facto Regeln, als die Null interpretiert wird FALSE und irgendein ein Wert ungleich Null wird interpretiert als TRUEDu solltest Vergleichen Sie niemals boolesch aussehende Ausdrücke mit TRUE oder FALSE. Beispiele:

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

Wieso den? Weil viele Programmierer die Abkürzung des Behandelns verwenden intist wie bools. Sie sind nicht gleich, aber Compiler erlauben es im Allgemeinen. So ist es zum Beispiel vollkommen legal zu schreiben

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

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

0 wenn yourString == myString

<0 wenn yourString < myString

>0 wenn yourString > myString

Die obige Zeile kehrt also zurück TRUE nur 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 oft im Produktionscode, und viele erfahrene Programmierer schwören auf sie: Sie funktionieren, einige sind kürzer als ihre (umständlich?) korrekten 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.

Lohnt sich das nicht?

  • (1==1) ist nicht tragbarer als 1. Die eigenen Regeln des Compilers sind die der C-Sprache, die bezüglich der Semantik von Gleichheits- und Vergleichsoperatoren klar und eindeutig ist. Ich habe noch nie gesehen, dass ein Compiler dieses Zeug falsch gemacht hat.

    – Keith Thompson

    9. Juni 2013 um 21:25 Uhr

  • Eigentlich der Wert, der von zurückgegeben wird strcmp ist bekanntermaßen kleiner, gleich oder größer als 0. Es ist nicht garantiert, dass es -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) == TRUE dann a > b aber die umgekehrte Implikation gilt möglicherweise nicht.

    – Maciej Piechotka

    10. Juni 2013 um 1:23 Uhr

  • @KeithThompson – Vielleicht war “Portabilität” der falsche Begriff. Aber Tatsache bleibt, dass (1==1) ein boolescher Wert ist; 1 nicht.

    – Adam Liss

    10. Juni 2013 um 1:31 Uhr

  • @AdamLiss: In C, (1==1) und 1 sind beides konstante Ausdrücke des Typs int mit dem Wert 1. Sie sind semantisch identisch. Ich nehme an, Sie können Code schreiben, der für Leser geeignet ist, die das nicht wissen, aber wo endet das?

    – Keith Thompson

    10. Juni 2013 um 1:40 Uhr

  • ‘nicht’ 5 ist tatsächlich -6 auf Bitebene.

    – woliveirajr

    10. Juni 2013 um 12:47 Uhr

Benutzeravatar von Kaz
Kaz

Das (1 == 1) Trick ist nützlich für die Definition TRUE auf eine Weise, die für C transparent ist, aber eine bessere Eingabe in C++ ermöglicht. Derselbe 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.

In C-Übersetzungseinheiten, 1 == 1 hat genau die gleiche Bedeutung wie 1; und 1 == 0 hat die gleiche Bedeutung wie 0. In den C++-Übersetzungseinheiten 1 == 1 Typ hat bool. Also die TRUE Ein so definiertes Makro lässt sich besser in C++ integrieren.

Ein Beispiel dafür, wie es sich besser integrieren lässt, ist zum Beispiel if function foo hat Überladungen für int und für booldann foo(TRUE) wird die wählen bool Überlast. Wenn TRUE ist nur definiert als 1dann funktioniert es in C++ nicht gut. foo(TRUE) will die int Überlast.

Natürlich C99 eingeführt bool, trueund false und diese können in Header-Dateien verwendet werden, die mit C99 und mit C funktionieren.

Jedoch:

  • diese Praxis des Definierens TRUE und FALSE wie (0==0) und (1==0) älter als 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 Kleinschreibung true, false und bool stattdessen.

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

Davon abgesehen, die 0==0 trick wurde (wird?) von einigen Programmierern sogar in Code verwendet, der nie dazu gedacht war, in irgendeiner Weise mit C++ zu interagieren. 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, hier ist 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

Zu der Frage aus den Kommentaren, wie überladene C++-Funktionen für die gemischte C- und C++-Programmierung relevant sind. Diese veranschaulichen nur einen Typunterschied. Ein triftiger Grund für den Wunsch nach a true konstant zu sein bool wenn es als C++ kompiliert wird, dient es der sauberen Diagnose. Auf den höchsten Warnstufen könnte uns ein C++-Compiler vor einer Konvertierung warnen, wenn wir eine ganze Zahl als a übergeben bool Parameter. Ein Grund für das Schreiben in Clean C ist nicht nur, dass unser Code besser portierbar ist (da er von C++-Compilern verstanden wird, nicht nur von C-Compilern), sondern wir können von den diagnostischen Meinungen von C++-Compilern profitieren.

  • Ausgezeichnete und unterschätzte Antwort. Es ist überhaupt nicht offensichtlich, dass die beiden Definitionen von TRUE wird sich unter C++ unterscheiden.

    – Benutzer4815162342

    9. Juni 2013 um 20:35 Uhr

  • Wie sind überladene Funktionen für Code relevant, der sowohl als C als auch als C++ kompiliert wird?

    – Keith Thompson

    9. Juni 2013 um 21:19 Uhr

  • @KeithThompson Es geht nicht nur um das Überladen, sondern um das richtige Tippen im Allgemeinen. Überladen ist nur das praktischste Beispiel, wenn es ins Spiel kommt. Natürlich C++-Code ohne Überladungen, Templates und all das “komplizierte” Zeug entfernt “C-Kompatibilität” kümmert sich nicht wirklich um Typen, aber das bedeutet nicht, dass man die stürzen sollte konzeptionell Typbeschränkungen in einer bestimmten Sprache.

    – Christian Rau

    10. Juni 2013 um 10:47 Uhr


  • @ChristianRau: Was meinst du mit “kümmert sich nicht wirklich um Typen”? Typen sind von zentraler Bedeutung für die C-Sprache; Jeder Ausdruck, Wert und jedes Objekt in einem C-Programm hat einen wohldefinierten Typ. Wenn Sie in C etwas anders definieren wollen als in C++ (in der Selten Fällen, in denen Sie tatsächlich Code schreiben müssen, der sowohl als C als auch als C++ kompiliert wird), können Sie verwenden #ifdef __cplusplus um Ihre Absicht viel klarer auszudrücken.

    – Keith Thompson

    10. Juni 2013 um 14:55 Uhr

  • @KeithThompson Ja, ich weiß, wie wichtig Typen sind. Es ist nur so, dass ohne all das typbewusste Zeug wie Überladen und Vorlagen, Dinge wie die Unterscheidung zwischen bool und int spielen in der Praxis keine große Rolle, da sie implizit ineinander konvertierbar sind (und in C eigentlich “das Gleiche”beachten Sie jedoch die Anführungszeichen) und es gibt nicht viele Situationen, in denen Sie dies wirklich tun müssen ausräumen zwischen den beiden. “wenig” War wohl zu schwer, “viel weniger im Vergleich zu Code, der Vorlagen verwendet und überlädt” wäre vielleicht besser gewesen.

    – Christian Rau

    10. Juni 2013 um 15:35 Uhr


Benutzeravatar von ouah
ouah

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

ist äquivalent zu

#define TRUE  1
#define FALSE 0

in C.

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

Es gibt absolut keinen Grund, die erste Form zu verwenden. Beachten Sie, dass die erste Form jedoch nicht weniger effizient ist, da bei fast allen Compilern ein konstanter Ausdruck zur Kompilierzeit und nicht zur Laufzeit ausgewertet wird. Dies ist nach dieser Regel erlaubt:

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

PC-Lint gibt sogar eine Meldung aus (506, konstanter Wert boolean), wenn Sie kein Literal für verwenden TRUE und FALSE Makros:

Für C, TRUE sollte definiert werden 1. Andere Sprachen verwenden jedoch andere Mengen als 1, so dass einige Programmierer das glauben !0 geht auf Nummer sicher.

Auch in C99, die stdbool.h Definitionen für boolesche Makros true und false direkt Literale verwenden:

#define true   1
#define false  0

Neben C++ (bereits erwähnt) gibt es einen weiteren Vorteil für statische Analysewerkzeuge. Der Compiler beseitigt alle Ineffizienzen, aber ein statischer Analysator kann seine eigenen abstrakten Typen verwenden, um zwischen Vergleichsergebnissen und anderen ganzzahligen Typen zu unterscheiden, sodass er implizit weiß, dass TRUE das Ergebnis eines Vergleichs sein muss und nicht als kompatibel angenommen werden sollte mit einer Ganzzahl.

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 sein könnte & und &&oder sie haben ihre Operatorpriorität verpfuscht.

  • Das ist ein guter Punkt, und vielleicht können einige dieser Tools sogar dummen Code abfangen if (boolean_var == TRUE) durch Erweiterung zu if (boolean_var == (1 == 1)) was dank der erweiterten Typinfo der (1 == 1) Knoten fällt in das Muster if (<*> == <boolean_expr>).

    – Kas

    9. Juni 2013 um 18:06 Uhr

Der praktische Unterschied ist keiner. 0 ausgewertet wird false und 1 ausgewertet wird true. Die Tatsache, dass Sie a verwenden Boolescher Ausdruck (1 == 1) oder 1definieren true, spielt keine Rolle. Sie werden beide bewertet int.

Beachten Sie, dass die C-Standardbibliothek einen speziellen Header zum Definieren von booleschen Werten bereitstellt: stdbool.h.

  • Das ist ein guter Punkt, und vielleicht können einige dieser Tools sogar dummen Code abfangen if (boolean_var == TRUE) durch Erweiterung zu if (boolean_var == (1 == 1)) was dank der erweiterten Typinfo der (1 == 1) Knoten fällt in das Muster if (<*> == <boolean_expr>).

    – Kas

    9. Juni 2013 um 18:06 Uhr

Benutzeravatar von capiggue
capiggue

Wir kennen den genauen Wert, dem TRUE entspricht, nicht und die Compiler können ihre eigenen Definitionen haben. Was Sie also bevorzugen, ist, die interne des Compilers für die Definition zu verwenden. Dies ist nicht immer notwendig, wenn Sie gute Programmiergewohnheiten haben, kann aber Probleme für einen schlechten Programmierstil vermeiden, zum Beispiel:

wenn ( (a > b) == WAHR)

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

  • In C, die > Operator liefert immer 1 für wahr, 0 für falsch. Es besteht keine Möglichkeit, dass ein C-Compiler dies falsch macht. Gleichstellungsvergleiche zu TRUE und FALSE sind schlechter Stil; das obige ist deutlicher geschrieben als if (a > b). Aber die Idee, dass verschiedene C-Compiler wahr und falsch unterschiedlich behandeln können, ist einfach falsch.

    – Keith Thompson

    14. Oktober 2013 um 22:52 Uhr

1424810cookie-checkWarum #define TRUE (1==1) in einem booleschen C-Makro statt einfach als 1?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy