Kann eine C-Makrodefinition auf andere Makros verweisen?

Lesezeit: 5 Minuten

Benutzeravatar von llakais
llakais

Ich versuche herauszufinden, ob so etwas (geschrieben in C):

#define FOO 15
#define BAR 23
#define MEH (FOO / BAR)

ist erlaubt? Ich möchte, dass der Präprozessor jede Instanz von ersetzt

MEH

mit

(15 / 23)

aber ich bin mir nicht sicher ob das geht. Wenn der Präprozessor den Code nur einmal durchläuft, glaube ich nicht, dass es so funktionieren würde, wie ich es möchte.

Ich habe mehrere ähnliche Beispiele gefunden, aber alle waren wirklich zu kompliziert für mich, um sie zu verstehen. Wenn mir jemand bei diesem einfachen Problem helfen könnte, wäre ich unendlich dankbar!

  • Bevor ich Ihre Frage gelesen habe, dachte ich, Sie fragen, ob eine Makrodefinition ein anderes Makro definieren kann, z #define FOO(x) #define BAR x. Die Antwort auf diese Frage (die Sie eigentlich nicht gestellt haben) lautet nein; ein Makro Definition kann keine weiteren Präprozessordirektiven enthalten. Ich werde Ihren Titel bearbeiten, um klarer zu machen, was Sie fragen.

    – Keith Thompson

    1. November 2011 um 21:01 Uhr

  • Google X-Makros, viel Spaß.

    – Jake

    1. November 2011 um 21:47 Uhr

Kurze Antwort ja. Sie können solche Definitionen und Makros verschachteln – so viele Ebenen, wie Sie wollen, solange es nicht rekursiv ist.

  • Ist die Reihenfolge wichtig? dürfen MEH zuerst #definiert werden?

    – David Heffernan

    1. November 2011 um 21:04 Uhr

  • Ich kann den Standard nicht zitieren, aber ich denke, es spielt keine Rolle. Ich mache seit langem sowohl vorwärts als auch rückwärts verschachtelte Makrodefinitionen und es funktioniert in VC++, GCC, ICC usw.

    – Mystisch

    1. November 2011 um 21:07 Uhr

  • @DavidHeffernan: Die Reihenfolge ist nicht wichtig, alles, was zählt, ist zu haben MEH, FOO und BAR alle definierten wann MEH erscheint. Der Präprozessor ersetzt zuerst MEH mit (FOO / BAR)dann neu beginnen, zuerst ersetzen FOO mit 15 dann BAR mit 23

    – Schmutziges Eis

    1. November 2011 um 21:09 Uhr

  • (Bestätigung der Reihenfolge spielt keine Rolle) Beachten Sie, dass Sie selbst definieren können MEH zuerst (basierend auf FOO und BAR), dann FOO und BAR danach, und es funktioniert immer noch gut. Dies scheint zunächst so, als ob es nicht funktionieren sollte, aber auch hier funktioniert es einwandfrei. Daher muss der Präprozessor entweder jede Definition während des Vorgangs in einer Hash-Tabelle speichern und Elemente aktualisieren, wenn er neue Definitionen findet, oder er muss mehrere Durchgänge durchführen. Egal ob wie es tut es, es funktioniert tatsächlich, unabhängig von der Reihenfolge der Definitionen.

    – Gabriel Staples

    4. März 2019 um 21:40 Uhr


Die Antwort ist “Ja”, und zwei andere Leute haben das richtig gesagt.

Wie für warum Die Antwort ist ja, die blutigen Details sind in der C-Standard, Abschnitt 6.10.3.4, „Nachscannen und erneutes Ersetzen“. Das OP profitiert möglicherweise nicht davon, aber andere könnten interessiert sein.

6.10.3.4 Erneutes Scannen und weiterer Austausch

Nachdem alle Parameter in der Ersetzungsliste ersetzt wurden und die #- und ##-Verarbeitung stattgefunden hat, werden alle Placemarker-Vorverarbeitungstoken entfernt. Dann wird die resultierende Vorverarbeitungstokensequenz zusammen mit allen nachfolgenden Vorverarbeitungstoken der Quelldatei erneut abgetastet, um weitere Makronamen zu ersetzen.

Wenn der Name des zu ersetzenden Makros während dieses Scans der Ersetzungsliste gefunden wird (ohne die restlichen Vorverarbeitungstoken der Quelldatei), wird er nicht ersetzt. Wenn darüber hinaus verschachtelte Ersetzungen auf den Namen des zu ersetzenden Makros stoßen, wird es nicht ersetzt. Diese nicht ersetzten Makronamen-Vorverarbeitungstoken stehen nicht mehr für einen weiteren Ersatz zur Verfügung, selbst wenn sie später in Kontexten (erneut) untersucht werden, in denen dieses Makronamen-Vorverarbeitungstoken ansonsten ersetzt worden wäre.

Die resultierende vollständig durch Makros ersetzte Vorverarbeitungstokensequenz wird nicht als Vorverarbeitungsanweisung verarbeitet, selbst wenn sie einer solchen ähnelt, aber alle unären Pragma-Operatorausdrücke darin werden dann wie in 6.10.9 unten angegeben verarbeitet.

Ja, es wird funktionieren.


Aber zu Ihrer persönlichen Information, hier sind einige vereinfachte Regeln zu Makros, die Ihnen helfen könnten (sie liegen außerhalb des Geltungsbereichs, werden Ihnen aber wahrscheinlich in Zukunft helfen). Ich werde versuchen, es so einfach wie möglich zu halten.

  • Die Definitionen werden in der Reihenfolge “definiert”, in der sie eingeschlossen/gelesen werden. Das bedeutet, dass Sie kein Define verwenden können, das zuvor nicht definiert wurde.

  • Nützliches Präprozessor-Schlüsselwort: #define, #undef, #else, #elif, #ifdef, #ifndef, #if

  • Sie können jedes andere zuvor #define in Ihrem Makro verwenden. Sie werden ausgebaut. (wie in deiner Frage)

  • Funktionsmakrodefinitionen akzeptieren zwei spezielle Operatoren (# und ##)

operator # stringiere das Argument:

#define str(x) #x
str(test); // would translate to "test"

Operator ## verkettet zwei Argumente

#define concat(a,b) a ## b
concat(hello, world); // would translate to "helloworld"

Es gibt auch einige vordefinierte Makros (aus der Sprache), die Sie verwenden können:

__LINE__, __FILE__, __cplusplus, etc

Sehen Sie sich dazu Ihren Compiler-Abschnitt an, um eine umfangreiche Liste zu erhalten, da es nicht “plattformübergreifend” ist.

  • Achten Sie auf die Makroerweiterung

Sie werden sehen, dass beim Definieren von Makros ein Protokoll aus runden Klammern “()” verwendet wird. Der Grund dafür ist, dass beim Aufrufen eines Makros es “wie es ist” erweitert wird.

#define mult(a, b) a * b
mult(1+2, 3+4); // will be expanded like: 1 + 2 * 3 + 4 = 11 instead of 21.
mult_fix(a, b) ((a) * (b))

  • Das ## Betreiber ist wirklich ordentlich! ‘Macht Namensmanipulation möglich ….

    – Elliot

    22. Oktober 2020 um 3:28 Uhr

Ja, und es gibt noch einen weiteren Vorteil dieser Funktion. Sie können einige Makros undefiniert lassen und ihren Wert festlegen als Name eines anderen Makros im Kompilierungsbefehl.

#define STR "string"
void main() { printf("value=%s\n", VALUE); }

In der Befehlszeile können Sie sagen, dass das Makro “VALUE” den Wert eines anderen Makros “STR” übernimmt:

$ gcc -o test_macro -DVALUE=STR main.c
$ ./test_macro

Ausgabe:

value=string

Dieser Ansatz funktioniert auch für den MSC-Compiler unter Windows. Ich finde es sehr flexibel.

Ich möchte einen Fallstrick hinzufügen, der mich zum Stolpern gebracht hat.

Makros im Funktionsstil kann nicht mach das.

Beispiel, das bei Verwendung nicht kompiliert wird:

#define FOO 1
#define FSMACRO(x) FOO + x

  • Funktioniert einwandfrei, was ist das Problem?

    – Jani

    10. Januar 2019 um 12:23 Uhr

  • Vielleicht funktioniert es nicht auf einem bestimmten Compiler. @Stefan, welchen Compiler hast du verwendet, der das nicht zulässt?

    – OmarL

    12. Oktober 2021 um 10:18 Uhr

Benutzeravatar von Mike Dinescu
Mike Dinescu

Ja, das wird unterstützt. Und viel benutzt!

Eine wichtige Sache, die Sie jedoch beachten sollten, ist, dass Sie den Ausdruck in Klammern setzen, da Sie sonst auf unangenehme Probleme stoßen könnten!

#define MEH FOO/BAR

// vs

#define MEH (FOO / BAR)

// the first could be expanded in an expression like 5 * MEH to mean something 
//   completely different than the second

  • Funktioniert einwandfrei, was ist das Problem?

    – Jani

    10. Januar 2019 um 12:23 Uhr

  • Vielleicht funktioniert es nicht auf einem bestimmten Compiler. @Stefan, welchen Compiler hast du verwendet, der das nicht zulässt?

    – OmarL

    12. Oktober 2021 um 10:18 Uhr

1415270cookie-checkKann eine C-Makrodefinition auf andere Makros verweisen?

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

Privacy policy