Makros mit Parametern (C++)

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Der Präprozessor - Index


Für die Definition eines Makros mit Parametern gilt die folgende Syntax:

#define makro_bezeichner(<argument_liste>) <symbolfolge>

Zwischen makro_bezeichner und der öffnenden runden Klammer ( sind keine Whitespaces zulässig. Die optionale argument_liste ist eine durch Komma getrennte Aufzählung von Bezeichnern, ähnlich der Argumentliste einer C-Funktion. Ein Komma innerhalb von runden zusammengehörigen Klammern in der Argumentliste wird jedoch als Teil des Arguments und nicht als Begrenzer des Arguments betrachtet. Jeder durch Komma getrennte Bezeichner stellt ein formales Argument oder einen Platzhalter dar.

Ein solches Makro ruft man mit

makro_bezeichner<whitespace>(<liste_der_tatsächlichen_argumente>)

im nachfolgenden Programmcode auf. Die Syntax ist identisch mit der eines Funktionsaufrufs. Es gibt jedoch einige wichtige semantische Unterschiede, Seiteneffekte und mögliche "Fallgruben".

Die optionale liste_der_tatsächlichen_argumente muss exakt dieselbe Anzahl von durch Komma getrennten Symbolfolgen (tatsächliche Argumente genannt) enthalten wie die formale argument_liste in der #define-Zeile. Für jedes formale Argument muss es ein tatsächliches Argument geben. Falls die Zahl der Argumente in beiden Listen unterschiedlich ist, wird eine Fehlermeldung ausgegeben.

Ein Makroaufruf bewirkt zwei Ersetzungsvorgänge. Zuerst werden der Makrobezeichner und die in Klammern eingeschlossenen Argumente durch die Symbolfolge ersetzt. Danach werden alle in der Symbolfolge auftretenden formalen Argumente durch die jeweiligen tatsächlichen Argumente aus liste_der_tatsächlichen_argumente ersetzt.

Wie bei einfachen Makrodefinitionen erfolgt nun ein erneutes Durchsuchen, um etwaige eingebettete Makrobezeichner zu finden und zu erweitern.

Verschachteln von Klammern und Kommas

Die liste_der_tatsächlichen_argumente darf verschachtelte runde Klammern enthalten, sofern sie korrekt paarig sind. Auch Kommas innerhalb von Anführungszeichen werden nicht als Argumentbegrenzer interpretiert.

#define ERRMSG(x, str) printf("Fehler: %d \n%s", x, str) 
#define SUM(x,y)  ((x) + (y)) 
: 
ERRMSG(2, "Drücken Sie Eingabe, dann ESC"); 
/*wird erweitert zu:  printf("Fehler: %d \n%s", 2, "Drücken Sie Eingabe, dann ESC"); */ 
return SUM(f(i, j), g(k, l)); 
/*wird erweitert zu:  return ( (f(i, j)) + (g(k, l)) ); */

Seiteneffekte und andere Gefahren

Die Ähnlichkeiten zwischen Funktions- und Makroaufrufen verdecken oft deren Unterschiede. Bei einem Makroaufruf gibt es keine integrierte automatische Typprüfung, so dass eine Nichtübereinstimmung der Datentypen von formalen und tatsächlichen Argumenten ohne direkte Fehlermeldung äußerst merkwürdige, schwer nachzuvollziehende Ergebnisse verursachen kann. Makroaufrufe können auch zu unerwünschten Seiteneffekten führen, insbesondere dann, wenn ein tatsächliches Argument mehrmals ausgewertet wird.

Vergleichen Sie CUBE und cube im folgenden Beispiel.

int cube(int x) { 
   return x*x*x; 
  } 
#define CUBE(x)  ( (x) * (x) * (x) ) 
... 
int b = 0, a = 3; 
b = cube(a++); 
/* an cube() wird das tatsächliche Argument a = 3 übergeben; daher ist b = 27; jetzt ist a = 4 */ 
a = 3; 
b = CUBE(a++); 
/* wird erweitert zu ( (a++) * (a++) * (a++) ); wieder ist b = 27, aber jetzt ist a = 6 */

Umwandeln von tatsächlichen Argumenten in Strings mit dem #-Operator

Sie können das Zeichen # vor ein formales Makroargument setzen, um das tatsächliche Argument nach dem Ersetzen in einen String umzuwandeln.

Bei der Definition:

#define TRACE(flag) printf(#flag "=%d\n", flag)

wird der Quelltextausschnitt

int highval = 1024; 
TRACE(highval);

zu

int highval = 1024; 
printf("highval" "=%d\n", highval);

was behandelt wird als

int highval = 1024; 
printf("highval=%d\n", highval);


Siehe auch