Ein Makro ist ein Name, den der Präprozessor vor dem Lauf des Compilers durch Text ersetzt. Das ist der gesamte Mechanismus, und jede Regel auf dieser Seite folgt daraus: Makros haben keine Typen, respektieren keinen Gültigkeitsbereich, werten keine Argumente aus und wissen nicht, was ein Funktionsaufruf ist. Sie kopieren Text.
Sorgsam eingesetzt sind sie unentbehrlich - Portabilitätsschalter, Zusicherungsmakros, die die Zeilennummer einfangen, Konstanten zur Übersetzungszeit, die in Arraygrößen funktionieren. Unachtsam eingesetzt erzeugen sie Bugs, die unmöglich aussehen, bis du die Datei expandierst.
Objektartige Makros
Die einfachste Form definiert einen Namen, der für einen Wert steht:
Konventionell werden diese Namen in GROSS_MIT_UNTERSTRICH geschrieben, damit ein Leser auf einen Blick sieht, dass ein Symbol ein Makro und keine Variable ist. Diese Konvention zählt hier mehr als sonst, denn Makros ignorieren den Gültigkeitsbereich: Ein #define innerhalb einer Funktion gilt weiterhin für den Rest der Datei und schreibt bereitwillig eine gleichnamige Variable in einer anderen Funktion um.
#undef NAME entfernt eine Definition, sodass ein Name später ohne Warnung wegen Neudefinition neu definiert werden kann.
Funktionsartige Makros
Setze eine Parameterliste direkt hinter den Namen - ohne Leerzeichen vor der öffnenden Klammer -, und das Makro nimmt Argumente entgegen:
Die Leerzeichenregel ist ernst gemeint: #define SQUARE (x) ((x)*(x)) definiert ein objektartiges Makro namens SQUARE, dessen Ersatztext mit (x) beginnt. Der folgende Compilerfehler wird die Leerzeichen mit keinem Wort erwähnen.
Die Klammerregeln
Es gibt zwei, und beide existieren, weil der Rumpf in einen Ausdruck eingefügt wird, den du beim Schreiben des Makros nicht sehen kannst.
Regel 1: Klammere jeden Parameter ein. Ohne das reißt der Vorrang ein Argument auseinander, das selbst ein Ausdruck ist:
#define SQUARE_BAD(x) x * x
SQUARE_BAD(2 + 3) /* wird zu 2 + 3 * 2 + 3 == 11, nicht 25 */
Regel 2: Klammere den gesamten Rumpf ein. Ohne das reißt der umgebende Ausdruck das Ergebnis auseinander:
#define DOUBLE_BAD(x) (x) + (x)
10 / DOUBLE_BAD(5) /* wird zu 10 / (5) + (5) == 7, nicht 1 */
Führe beide Fehlschläge neben ihren korrigierten Fassungen aus:
Beide schlechten Fassungen kompilieren ohne eine einzige Warnung und liefern falsche Zahlen. Die Gewohnheit, die man sich zulegt, ist mechanisch: jeden Parameter einwickeln, dann das Ergebnis einwickeln. ((a) > (b) ? (a) : (b)) sieht nach Rauschen aus, und das Rauschen ist der Punkt.
Die Mehrfachauswertungsfalle
Klammern können dich vor der zweiten Gefahr nicht retten. Ein Makro fügt den Text seines Arguments an jeder Stelle ein, an der der Parameter auftaucht, ein Argument mit einer Nebenwirkung passiert also mehr als einmal:
Das Makro expandiert zu ((i++) > (j) ? (i++) : (j)). Der Vergleich erhöht i auf 6 und vergleicht 5 mit 3; der Wahr-Zweig erhöht i erneut auf 7 und liefert 6. m ist also 6 und i ist 7 - keines von beidem ist das, was „der größere von i und j" ergeben sollte. Eine Funktion max(i++, j) wäre korrekt, weil das Argument vor dem Aufruf einmal ausgewertet wird.
Dieselbe Falle erwischt alles mit einer Nebenwirkung: SQUARE(rand()) ruft rand() zweimal auf und multipliziert zwei verschiedene Zahlen. CHECK(read_byte()) verbraucht zwei Bytes.
Zwei Verteidigungen, nach Vorzug geordnet:
- Nimm eine echte Funktion. Brauchst du sie für mehrere Typen, schreibe eine pro Typ oder nimm eine
static inline-Funktion in einem Header. - Muss es ein Makro sein, dokumentiere laut, dass Argumente mehr als einmal ausgewertet werden, und halte die Aufrufstellen frei von Nebenwirkungen.
Mehrzeilige Makros und do-while(0)
Ein Makrorumpf darf sich über Zeilen erstrecken, wenn jede Zeile außer der letzten mit einem Rückstrich endet. Die naive Fassung sieht in Ordnung aus:
#define LOG_TWICE(msg) \
printf("%s\n", msg); \
printf("%s\n", msg)
Und dann geht sie still kaputt, an der einen Stelle, an der es zählt:
if (verbose)
LOG_TWICE("hallo");
else
printf("leise\n");
Nach der Ersetzung besitzt das if nur das erste printf, das zweite läuft bedingungslos, und das else hat nun kein passendes if mehr - ein Übersetzungsfehler, dessen Meldung nirgends Nützliches hinzeigt. Den Rumpf in schlichte geschweifte Klammern zu wickeln ist nicht besser: Das abschließende ; nach LOG_TWICE("hallo") wird zu einer leeren Anweisung, die das if beendet, und das else bricht erneut.
Das Idiom, das funktioniert, ist do { ... } while (0):
do { ... } while (0) ist eine einzelne Anweisung, es führt seinen Rumpf genau einmal aus und es verlangt ein Semikolon danach - die Aufrufstelle liest sich also wie ein gewöhnlicher Funktionsaufruf und verhält sich in jedem Kontrollflusszusammenhang auch so. Achte auf die Rückstriche: Ein verirrtes Leerzeichen hinter einem Rückstrich beendet die Fortsetzung und erzeugt einen rätselhaften Fehler.
Vordefinierte Makros und Zusicherungsmuster
Der Präprozessor definiert selbst mehrere Makros, und sie sind der Grund, warum manche Dinge schlicht keine Funktionen sein können:
__FILE__ und __LINE__ expandieren dort, wo sie geschrieben stehen, sie in ein Makro zu setzen fängt also die Position des Aufrufers ein. Eine Funktion könnte das nicht - innerhalb einer Funktion würden sie immer deren eigene Datei und Zeile melden. Genau so meldet das Standardmakro assert den Ort des fehlgeschlagenen Ausdrucks.
Weitere nützliche vordefinierte Namen: __DATE__, __TIME__ und __STDC_VERSION__ (zum Beispiel 201710L für C17).
Zwei wissenswerte Operatoren
Innerhalb eines Makrorumpfs macht # aus einem Parameter ein Zeichenkettenliteral („Stringizing") und ## klebt Token zusammen („Token-Pasting").
SHOW(width * height) wird zu printf("width * height" " = %d\n", (width * height)); - benachbarte Zeichenkettenliterale fügt der Compiler zusammen, ein Aufruf gibt also sowohl den Text des Ausdrucks als auch seinen Wert aus. Ein Debugging-Trick, den man sich merken sollte.
## ist seltener und taucht meist in codeerzeugenden Makros auf: #define MAKE_VAR(n) int var_##n macht aus MAKE_VAR(3) ein int var_3. Benutze es sparsam; vom Präprozessor gebaute Bezeichner lassen sich nicht nach Namen durchsuchen, was den Code schwer navigierbar macht.
Makros gegen Funktionen gegen const
Nimm das schwächste Werkzeug, das die Aufgabe erledigt:
const-Variable -const double PI = 3.14159;hat einen Typ, gehorcht dem Gültigkeitsbereich, taucht im Debugger auf und kann nicht von einem fremden Header neu definiert werden. Bevorzuge sie für Werte, die zur Laufzeit benutzt werden.enum-enum { MAX_USERS = 100 };liefert eine benannte Ganzzahlkonstante, die dort funktioniert, wo C eine Konstante zur Übersetzungszeit verlangt, mit Typ und Gültigkeitsbereich. Gut für ganzzahlige Grenzen.static inline-Funktion - typgeprüft, wertet jedes Argument genau einmal aus, und moderne Compiler binden sie genauso ein wie ein Makro. Das ist der richtige Ersatz für fast jedes funktionsartige Makro.- Makro - wenn du
__LINE__brauchst, wenn die Sache funktionieren muss, bevor es Typen gibt (Arraygrößen in älterem Code), wenn du Code mit bedingter Übersetzung umschaltest, oder wenn du sich wiederholenden Code erzeugst.
Ein Feld, in dem Makros unangefochten bleiben, ist die Arraygröße, denn das Ergebnis muss eine Konstante zur Übersetzungszeit sein:
Dieses hat seinen eigenen bekannten Vorbehalt: Es ist nur für ein echtes Array korrekt. Übergib einen Zeiger - was ein Array wird, sobald man es einer Funktion reicht - und sizeof misst den Zeiger und liefert eine falsche, selbstbewusst stille Antwort.
Ein Makro debuggen
Wenn ein Makro sich falsch verhält, starre es nicht an. Expandiere es:
gcc -E program.c | tail -30
Der eingesetzte Text sagt dir sofort, ob das Problem eine fehlende Klammer, eine Doppelauswertung oder ein mit etwas anderem kollidierender Name ist. Kompiliere auch mit -Wall -Wextra - GCC und clang fügen „in expansion of macro"-Hinweise hinzu, die die gemeldete Fehlerzeile mit der Definition verbinden.
Als Nächstes: Header-Dateien, wo #define und #include zusammenkommen, damit ein Projekt über viele Quelldateien reichen kann, ohne dieselben Deklarationen zweimal einzufügen.
Häufig gestellte Fragen
Was ist ein Makro in C?
Ein benanntes Stück Text, das der Präprozessor vor der Übersetzung in deinen Quelltext einsetzt. #define MAX 100 macht aus jedem späteren MAX eine 100; #define SQUARE(x) ((x) * (x)) nimmt Argumente entgegen und setzt sie in ein Muster ein. Makros haben keine Typen und gehorchen keinen Gültigkeitsregeln - sie sind Textersetzung.
Warum brauchen C-Makros so viele Klammern?
Weil der Rumpf in umgebenden Code eingefügt und dann als Ganzes geparst wird. #define SQUARE(x) x * x macht aus SQUARE(2 + 3) den Ausdruck 2 + 3 * 2 + 3, also 11 statt 25. Jedes Argument und den gesamten Rumpf einzuklammern - ((x) * (x)) - lässt den eingesetzten Text so gruppieren, wie du es meintest, egal was drumherum steht.
Was ist der Unterschied zwischen einem Makro und einer Funktion in C?
Eine Funktion wird einmal übersetzt, typgeprüft und wertet jedes Argument genau einmal aus. Ein Makro wird an jeder Verwendungsstelle eingefügt, prüft nichts und darf ein Argument mehrfach auswerten - MAX(i++, j) kann i also zweimal erhöhen. Bevorzuge Funktionen (und const/enum für Konstanten); nimm Makros für das, was Funktionen nicht können, etwa __LINE__ einzufangen oder Code zu erzeugen.
Warum wickelt man ein mehrzeiliges Makro in do { ... } while (0)?
Damit es sich wie eine einzelne Anweisung verhält. Ein nackter { ... }-Rumpf geht kaputt, wenn ihm in einem if/else ein Semikolon folgt (das Semikolon beendet das if zu früh), und eine schlichte Anweisungsfolge geht in einem klammerlosen if kaputt. do { ... } while (0) ist eine Anweisung, die ein abschließendes Semikolon annimmt und ihren Rumpf genau einmal ausführt.