#define bezeichner ersatzliste #define bezeichner(bezeichner_liste) ersatzlisteIm ersten Fall wird eine symbolische Konstante, im zweiten Fall ein Makro definiert.
Die Direktive #define ersetzt jedes weitere Vorkommen von
bezeichner im Quelltext durch die als ersatzliste
angegebene Zeichenfolge.
Dieser Ersatz betrifft nur Terminalsymbole (also eigenständige Vorkommen
von bezeichner im Quelltext), nicht aber Zeichenfolgen, die in einem
Kommentar erscheinen, Teil eines anderen Bezeichners oder Teil einer
Stringkonstanten sind.
ersatzliste besteht aus einer Folge von Symbolen, es können
reservierte Wörter, Konstanten und vollständige Anweisungen
enthalten sein.
ersatzliste muß durch ein oder mehrere "weiße"
Leerzeichen (whitespaces) von bezeichner getrennt sein.
Leerzeichen, die ersatzliste vorangestellt wurden, sind
ebenso wie nachfolgende Leerzeichen Teil des Ersatztexts.
Wenn hinter bezeichner eine bezeichner_liste erscheint,
ersetzt die Direktive #define jedes Vorkommen von
bezeichner(bezeichner_liste) mit einer Version von
ersatzliste, bei der die dort angegebenen Parameter den aktuellen
Variablennamen angepaßt werden:
Die Bezeichnerliste stellt in diesem Fall eine Parameterliste
für den Makro dar.
Wenn ein Makro mit Parametern existiert, werden weitere Vorkommen des
verwendeten Bezeichners zusammen mit einer entsprechenden Bezeichnerliste
als Makroaufruf interpretiert, der zu einer entsprechenden Erweiterung
(also dem Einsetzen der Ersatzliste in den Quelltext) führt.
Der Präprozessor paßt bei derartigen Erweiterungen die in der
Ersatzliste
verwendeten Bezeichner an: Jeder Parameter aus dieser Liste, dem kein
String- (#), Zeichen- (#@) oder Symboloperator (##) vorausgeht und kein
Symboloperator folgt, wird durch den im Makroaufruf verwendeten
Variablennamen ersetzt. Sollte der Makroaufruf seinerseits Makrobezeichner
als Parameter verwenden, dann werden diese Makros vor dem Einsetzen in die
Ersatzliste erweitert.
Parameternamen in der Ersatzliste dienen als Platzhalter für die bei einer
Erweiterung des Makros einzusetzenden aktuellen Bezeichner und werden ohne
Einschränkungen durch den Kontext ersetzt: Ein Parametername kann bei
Bedarf mehrfach in der Ersatzliste erscheinen, eine bestimmte Reihenfolge
ist dabei nur durch die Logik des Makros selbst gefordert.
Da Makros im Gegensatz zu Funktionen nicht mit variabler Parameteranzahl
arbeiten können, muß ein Makroaufruf grundsätzlich so viele
aktuelle Parameter angeben, wie sie die Ersatzliste enthält.
Speziell bei komplexeren Parametern sollte mit entsprechender Klammerung
dafür gesorgt werden, daß keine Mißverständnisse in
Bezug auf die Auswertung und Zusammengehörigkeit der einzelnen
Ausdrücke entstehen.
Rekursive Erweiterungen finden bei Parameternamen nicht statt:
Wenn die Definition eines Makros xyz den Bezeichner xyz in der Ersatzliste
enthält, wird dieser Bezeichner nicht erneut erweitert - auch dann nicht,
wenn sich die Zeichenfolge xyz erst durch die Erweiterung eines anderen
Makros ergibt.
Die Parameter in der Bezeichnerliste einer Makrodefinition müssen durch
Kommas voneinander getrennt werden und innerhalb dieser Liste jeweils
eindeutig sein.
Beispiel:
#define MIN(x,y) ((x<y) ? x : y)Der Geltungsbereich der bei Makrodefinitionen verwendeten Bezeichner erstreckt sich ausschließlich auf die jeweilige Definition, d.h. er endet mit der Ersatzliste.
Zwischen dem Bezeichner des Makros und der öffnenden
Klammer darf kein Leerzeichen stehen.
Beispiel:
#define SQR(a) ( a * a) /* korrekt */ #define SQR (a) ( a * a) /* fuehrt zu Syntaxfehler */Makrodefinitionen können sich über mehrere Zeilen erstrecken. Die Zusammengehörigkeit der Zeilen müß durch einen Rückstrich \ (backslash) am Ende einer fortzusetzende Zeile angezeigt werden.
#define LONGTEXT "Dies ist ein langer Text, der auf " \
"der naechsten Zeile fortgesetzt wird."
Beispiel:
#define BREITE 80 #define LAENGE ( BREITE + 10 ) int var; var = LAENGE * 20; /* liefert var = ( 80 + 10 ) * 20; */Der Präprozessor operiert nur mit Zeichenketten, d.h. aus
LAENGE * 20
wird zunächst
( BREITE + 10 ) * 20
und dann
( 80 + 10 ) * 20
jedoch nicht 180 !
Fehlerquelle:
Klammern können bei der Notation von Ausdrücken sehr wichtig
sein !
#define BREITE 80 #define LAENGE BREITE + 10 int var; var = LAENGE * 20; /* liefert var = 80 + 10 * 20; */Während zuvor für var 180 als Wert berechnet wurde, ergibt sich jetzt 280 als Wert !
Da Parameternamen mehrfach in der Ersatzliste von Makros erscheinen können und der Präprozessor bei Erweiterungen jeweils den gesamten Ausdruck einsetzt, können Parameter mit Seiteneffekten unter Umständen zu unerwarteten Ergebnissen führen.
Beispiel:
#define NMAX 100 var=NMAX; /* liefert var=100; */Fehlerquelle:
#define NMAX = 100 var=NMAX; /* liefert var== 100; */
Makros können im Prinzip wie Funktionen benutzt werden, jedoch ist
die Arbeitsweise nicht identisch !
Das folgende Beispiel zeigt einen gefährlichen Seiteneffekt:
#define SQUARE(a) ( a * a) /* liefert das Quadrat */
int square(int a)
{
return ( a*a );
}
int main(void)
{
int a1 = 1, a2 = 1;
int b, c;
b = SQUARE(++a1); /* liefert c = ( ++a1 * ++a1 ); */
c = square(++a2);
return 0;
}
a1 wird nicht wie a2 einmal, sondern zweimal inkrementiert.
Während c bei der Ausführung des Programms den korrekten Wert
4 erhät, wird an b der Wert 6 zugewiesen. Der Präprozessor führt folgende Ersetzung durch:
c = ++a * ++a;Konsequent wird a nicht einmal, sondern zweimal erhöht: c bekommt anstelle von 4 den Wert 6 zugewiesen.
Definieren einer Kennung, die anzeigt, daß eine bestimmte Header-Datei
bereits eingefügt wurde (Vermeidung von Re-Definitionen).
Beispiel: Ausschnitt aus der Header-Datei stdio.h
#ifndef _STDIO_H #define _STDIO_H ... #endifDiese Technik wird in allen Header-Dateien des Laufzeitsystems eingesetzt.
Ein #define ohne Angabe einer Ersatzliste entfernt nachfolgende Vorkommen des jeweiligen Bezeichners komplett aus dem Quelltext, d.h. ersetzt sie durch ein einzelnes Leerzeichen. Der Bezeichner gilt auch in diesem Fall weiterhin als definiert: Prüfungen mit #if defined ergeben TRUE, auf erneute (nicht identische) Redefinitionen des Bezeichners reagiert der Compiler mit einer Fehlermeldung.
Beispiel:
Verstehen ältere Compiler das Schlüsselwort volatile
nicht, so kann durch
#define volatileerreicht werden, daßer der Compiler einen Quelltext erhält, der dieses Schlüsselwort nicht mehr enthält.
In C++ machen Inline-Funktionen und Deklarationen mit const die Direktive #define größtenteils überflüssig. Weitere Informationen über Makros und das Ersetzen von Text finden Sie unter den Stichworten Stringoperator, Zeichenoperator und Symboloperator.
Die Anwendung von Makros zur Neudefinition von C-Standardfunktionen birgt
ein gewisses Risiko in sich.
Beispiel:
#include <stdio.h>
#define sqrt(x) ((x)<0 ? sqrt(-x) : sqrt(x))
int main(void)
{
double x = 1.0, y = -1.0;
printf("x = %lf sqrt(x) = %lf\n", x, sqrt(x));
printf("y = %lf sqrt(y) = %lf\n", y, sqrt(y));
return 0;
}
Testergebnisse:
gcc, unter Solaris 2.3, AIX 3.2 und DOS
Warnung: type mismatch in implicit declaration for
built-in function sqrt
Korrektes Ergebnis
lcc, unter Solaris 2.3
Korrektes Ergebnis
cc, auf Motorola
Korrektes Ergebnis
xlc, unter AIX 3.2
Fehlerhaftes Ergebnis
MSC, unter DOS
Fehlerhaftes Ergebnis
Rekursive Makrodefinitionen sind in ANSI-C möglich.
Beispiel:
#define char unsigned charÄltere Compiler können bei rekursiven Makrodefinitionen in Schwierigkeiten kommen.
Einer symbolischen Konstanten (einem Makro) kann erst beim Compilieren ein Wert zugewiesen werden.
Beispiel:
#include <stdio.h>
int main(void)
{
printf("N = %d\n", N);
return 0;
}
Die Übersetzung des obigen Programms (Dateiname def.c) mittels
des Befehls
cc -o def def.cführt zu einer Fehlermeldung folgender Art: Undeclared identifier N.
cc -o def def.c -DN=6Das Programm kann jetzt übersetzt werden und liefert
N = 6Das obige Beispielprogramm sollte jedoch besser wie folgt formuliert werden:
#include <stdio.h>
int main(void)
{
#ifdef N
printf("N = %d\n", N);
#else
#error Symbolische Konstante N nicht definiert
#endif
return 0;
}
Durch die #error-Direktive wird ein fehlerhafter
Übersetzungslauf verhindert, falls N nicht definiert ist.
Einige Makros aus stdio.h
#define getchar() getc(stdin) #define putchar(x) putc(x,stdout) #define fgetpos(stream, pos) (((*(pos) = ftell(stream)) == -1) ? -1 : 0) #define fsetpos(stream, pos) (fseek((stream), *(pos), SEEK_SET))