Warum nur ein Makro definieren, wenn es noch nicht definiert ist?

Lesezeit: 5 Minuten

Benutzeravatar von Trevor Hickey
Trevor Hickey

In unserer gesamten C-Codebasis sehe ich jedes Makro wie folgt definiert:

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

#ifndef BEEPTRIM_ROLL_RATE_DEGPS
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#endif

#ifndef FORCETRIMRELEASE_HOLD_TIME_MS
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#endif

#ifndef TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f
#endif

Was ist der Grund dafür, diese Definitionsprüfungen durchzuführen, anstatt nur die Makros zu definieren?

#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f

Ich kann diese Praxis nirgendwo im Internet erklärt finden.

  • Das Ändern von Konstanten an anderer Stelle im Code funktioniert garantiert auf diese Weise. Wenn jemand anderswo eines dieser Makros definiert, werden sie nicht vom Präprozessor überschrieben, wenn er diese Datei analysiert.

    – Enzo Ferber

    4. September 2015 um 12:55 Uhr


  • Es ist ein Beispiel für das WET-Designprinzip.

    – Stark

    4. September 2015 um 12:59 Uhr

  • Habe eine Antwort mit einem Beispiel gepostet, versuche es zu kompilieren.

    – Enzo Ferber

    4. September 2015 um 13:04 Uhr

Dadurch können Sie die Makros beim Kompilieren überschreiben:

gcc -DMACRONAME=value

Als Voreinstellung werden die Definitionen in der Header-Datei verwendet.

Wie ich im Kommentar sagte, stellen Sie sich diese Situation vor:

foo.h

#define FOO  4

def.h

#ifndef FOO
#define FOO 6
#endif

#ifndef BAR
#define BAR 4
#endif

bar.c

#include "foo.h"
#include "defs.h"

#include <stdio.h>

int main(void)
{
    printf("%d%d", FOO, BAR);
    return 0;
}

Wird gedruckt 44.

Wenn jedoch die Bedingung ifndef nicht vorhanden war, würde das Ergebnis Kompilierungswarnungen der MACRO-Neudefinition sein und es wird gedruckt 64.

$ gcc -o bar bar.c
In file included from bar.c:2:0:
defs.h:1:0: warning: "FOO" redefined [enabled by default]
 #define FOO 6
 ^
In file included from bar.c:1:0:
foo.h:1:0: note: this is the location of the previous definition
 #define FOO 4
 ^

  • Dies ist Compiler-spezifisch. Das Neudefinieren eines objektähnlichen Makros ist illegal, es sei denn, die Neudefinition ist “gleich” (dafür gibt es eine technischere Spezifikation, aber das ist hier nicht wichtig). Unzulässiger Code erfordert eine Diagnose, und nachdem eine Diagnose (hier eine Warnung) ausgegeben wurde, kann der Compiler alles tun, einschließlich des Kompilierens des Codes mit implementierungsspezifischen Ergebnissen.

    – Peter Becker

    4. September 2015 um 23:50 Uhr

  • Wenn Sie widersprüchliche Definitionen für dasselbe Makro haben, nicht wahr? eher erhalten Sie die Warnung in den meisten Fällen? Anstatt stillschweigend die erste Definition zu verwenden (weil die zweite eine an ifdef um Neudefinitionen zu vermeiden).

    – Peter Cordes

    5. September 2015 um 5:33 Uhr


  • @PeterCordes Meistens Definitionen unter #infdefs werden als „Fallback“- oder „Default“-Werte verwendet. Grundsätzlich “wenn der Benutzer es konfiguriert hat, in Ordnung. Wenn nicht, verwenden wir einen Standardwert.”

    – Angew ist nicht mehr stolz auf SO

    5. September 2015 um 13:28 Uhr

  • @Angew: Ok, also wenn du welche hast #defines in einer Bibliothekskopfzeile, die Teil der ABI der Bibliothek sind, sollten Sie nicht wickel sie ein #ifndef. (Oder besser, verwenden Sie eine enum). Das wollte ich nur klarstellen #ifndef ist nur angemessen, wenn eine benutzerdefinierte Definition für etwas in einer Kompilationseinheit vorhanden ist, in einer anderen jedoch nicht. Wenn a.c enthält Header in einer anderen Reihenfolge als b.cerhalten sie möglicherweise unterschiedliche Definitionen von max(a,b)und eine dieser Definitionen könnte mit brechen max(i++, x), aber der andere könnte Temporäre in einem GNU-Anweisungsausdruck verwenden. Immer noch zumindest verwirrend!

    – Peter Cordes

    5. September 2015 um 23:24 Uhr

  • @PeterCordes Was ich in diesem Fall gerne mache, ist #ifdef FOO #error FOO already defined! #endif #define FOO x

    – Cole Tobin

    6. September 2015 um 7:41 Uhr

Ich kenne den Kontext nicht, aber dies kann verwendet werden, um dem Benutzer die Möglichkeit zu geben, die von diesen Makrodefinitionen festgelegten Werte zu überschreiben. Wenn der Benutzer explizit einen anderen Wert für eines dieser Makros definiert, wird dieser anstelle der hier verwendeten Werte verwendet.

Beispielsweise können Sie in g++ die verwenden -D Flag während der Kompilierung, um einen Wert an ein Makro zu übergeben.

Dies geschieht, damit der Benutzer der Header-Datei die Definitionen aus seinem/ihrem Code oder aus dem -D-Flag des Compilers überschreiben kann.

Jedes C-Projekt befindet sich in mehreren Quelldateien. Wenn Sie an einer einzelnen Quelldatei arbeiten, scheinen (und tatsächlich) die Prüfungen keinen Sinn zu haben, aber wenn Sie an einem großen C-Projekt arbeiten, ist es eine gute Praxis, nach vorhandenen Definitionen zu suchen, bevor Sie eine Konstante definieren. Die Idee ist einfach: Sie benötigen die Konstante in dieser bestimmten Quelldatei, aber sie wurde möglicherweise bereits in einer anderen definiert.

Benutzeravatar von LPs
LPs

Sie könnten über ein Framework / eine Bibliothek nachdenken, die dem Benutzer eine Standardvoreinstellung gibt, die es dem Benutzer ermöglicht, sie zu kompilieren und daran zu arbeiten. Diese Definitionen sind in verschiedenen Dateien verteilt und dem Endbenutzer wird empfohlen, seine config.h-Datei einzuschließen, in der er seine Werte konfigurieren kann. Wenn der Benutzer einige Definitionen vergessen hat, kann das System aufgrund der Voreinstellung weiterarbeiten.

Benutzeravatar der Community
Gemeinschaft

Verwenden

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

ermöglicht dem Benutzer, den Wert des Makros mit dem Befehlszeilenargument zu definieren (in gcc/clang/VS) -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f.

Es gibt noch einen weiteren wichtigen Grund. Es ist ein Fehler, ein Präprozessormakro anders neu zu definieren. Siehe diese Antwort auf eine andere SO-Frage. Ohne das #ifndef check, sollte der Compiler einen Fehler erzeugen, wenn -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f wird als Befehlszeilenargument beim Compileraufruf verwendet.

1420570cookie-checkWarum nur ein Makro definieren, wenn es noch nicht definiert ist?

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

Privacy policy