In meiner aktuellen Codebasis sehe ich folgendes Muster:
#if SOMETHING_SUPPORTED+0 != 0
...
#endif
Leider ist dies eine sehr alte Codebasis und niemand weiß, wie und warum es angefangen hat. Ich glaube, es begann in C und wurde langsam mit Klassen in C umgewandelt und jetzt tendiert es zu C++
Ich sehe keinen offensichtlichen Vorteil darin, das vorherige Konstrukt anstelle des “Klassikers” zu verwenden, aber vielleicht fehlt mir etwas:
#if SOMETHING_SUPPORTED
...
#endif
Weißt du, warum würde man verwenden #if MACRO+0 != 0
Anstatt von #if MACRO
?
Der Hinweis hier ist, dass die Codebasis sehr alt ist.
Dieser Trick existiert wahrscheinlich, weil der Code einmal auf einen Compiler mit einem sehr alten Präprozessor portiert wurde, der nicht behandelt wird nicht definiert Makros als 0 im Präprozessor #if
Bedingungen.
Das heißt, ab 1989 ANSI C wurde standardisiert, dass, wenn wir haben:
#if foo + bar – xyzzy
Die Direktive unterliegt der Makroersetzung, sodass if foo
, bar
oder xyzzy
Makros sind, werden sie ersetzt. Dann werden alle verbleibenden Bezeichner, die nicht ersetzt wurden, durch ersetzt 0
. Also wenn foo
ist definiert als 42
aber bar
und xyzzy
überhaupt nicht definiert sind, erhalten wir:
#if 42 + 0 - 0
und nicht, sagen wir, schlechte Syntax:
#if 42 + -
oder ein anderes Verhalten, wie Diagnosen über bar
nicht definiert.
Auf einem Präprozessor, wo undefinierte Makros als Leerzeichen behandelt werden, #if SOMETHING_SUPPORTED
erweitert sich auf gerade #if
was dann falsch ist.
Dies ist die einzige Möglichkeit, dies zu tun IDENT+0
Trick macht wirklich Sinn. Sie würden dies einfach niemals tun wollen, wenn Sie sich darauf verlassen können, dass die Vorverarbeitung ISO-C-konform ist.
Der Grund ist, dass wenn SOMETHING_SUPPORTED
Numerische Werte erwartet werden, ist es irrtümlich unsinnig, es einfach als Leerzeichen zu definieren. Idealerweise möchten Sie feststellen, wann dies geschehen ist, und die Kompilierung mit einer Diagnose stoppen.
Zweitens, wenn Sie tun Wenn Sie eine solche verwirrende Verwendung unterstützen, möchten Sie mit ziemlicher Sicherheit, dass sich ein explizit definiertes, aber leeres Symbol so verhält, als hätte es den Wert 1 und nicht den Wert 0. Andernfalls erstellen Sie eine Falle. Jemand könnte dies auf der Compiler-Befehlszeile tun:
-DSOMETHING_SUPPORTED=$SHELL_VAR # oops, SHELL_VAR expanded to nothing
oder im Code:
#define SOMETHING_SUPPORTED /* oops, forgot "1" */
Niemand wird es tun hinzufügen a #define
oder -D
für ein Symbol mit der Absicht, sich zu drehen aus die Funktion, die es steuert! Der Programmierer, der a einfügt #define SOMETHING_SUPPORTED
ohne das 1
wird durch das Verhalten von überrascht sein
#if SOMETHING_SUPPORTED+0
wodurch das Material übersprungen wird, das aktiviert werden sollte.
Aus diesem Grund vermute ich, dass nur wenige C-Programmierer, die dies lesen, jemals eine solche Verwendung gesehen haben, und warum ich vermute, dass es sich nur um eine Problemumgehung für das Verhalten des Präprozessors handelt, dessen beabsichtigter Effekt darin besteht, den Block if zu überspringen SOMETHING_SUPPORTED
wird vermisst. Die Tatsache, dass es eine “Programmiererfalle” legt, ist nur ein Nebeneffekt der Problemumgehung.
Um ein solches Präprozessorproblem zu umgehen, ohne eine Programmierfalle zu erstellen, muss man irgendwo früh in der Übersetzungseinheit Folgendes haben:
#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif
und dann woanders einfach verwenden #if SOMETHING_SUPPORTED
. Vielleicht ist dieser Ansatz dem ursprünglichen Programmierer nicht in den Sinn gekommen, oder vielleicht hat dieser Programmierer das gedacht +0
trick war ordentlich und legte Wert auf seine Geschlossenheit.
#if X+0 != 0
ist anders als #if X
in dem Fall wo X
als leer definiert ist (Hinweis: Dies ist anders als im Fall von X
nicht definiert), zB:
#define X
#if X // error
#if X+0 != 0 // no error; test fails
Es ist sehr üblich, leere Makros zu definieren: Die Projektkonfiguration kann einen gemeinsamen Header generieren, der eine Reihe von Zeilen enthält #define USE_FOO
, #define USE_BAR
Funktionen zu aktivieren, die das System unterstützt, und so weiter.
Das != 0
ist überflüssig, der Code hätte einfach sein können #if X+0
.
Also, der Vorteil der Verwendung #if X+0
ist das wenn X
als leer definiert ist, wird die Kompilierung mit dem übersprungenen Block fortgesetzt, anstatt einen Fehler auszulösen.
Ob das eine gute Idee ist, darüber lässt sich streiten, würde ich persönlich nutzen #ifdef
für boolesche Makros wie USE_SOME_FEATURE
und #if
für Makros, bei denen der Wert beispielsweise ein Bereich von ganzen Zahlen sein kann; und ich möchte einen Fehler sehen, wenn ich versehentlich verwende #if
mit etwas, das auf leer definiert ist.
Machen wir einen Tisch!
X #if X #if X+0 != 0
<undef> false false
<empty> error false
0 false false
1 true true
2 true true
a false false
xyz false false
12a error error
12 a error error
Der einzige Unterschied, den wir (dank der Kommentatoren) gefunden haben, ist der Fall, in dem X definiert ist, aber keinen Wert hat (wie eine leere Zeichenfolge). Ich habe die noch nie gesehen +0 != 0
Variante vor.
Es ist eine andere Art zu schreiben
#if defined(MACRO) && MACRO != 0
Das +0 ist da, um sicherzustellen, dass das Ergebnis eine Zahl ist. Wenn MACRO
nicht definiert ist, vermeidet es den Syntaxfehler, der daraus resultieren würde #if MACRO != 0
.
Zeug von schlechter Qualität, aber leg dich nicht damit an, es sei denn, du musst.
Könnte verwandt sein: Test auf leere Makrodefinition
– t.niese
5. Mai 2016 um 10:22 Uhr
Das
!= 0
ist sicher überflüssig– MM
5. Mai 2016 um 10:30 Uhr