Präprozessor-Makros in C wurden als Teil der Sprache entwickelt, um die Portabilität, Lesbarkeit und Anpassungsfähigkeit des Quellcodes zu gewährleisten. Mit den Direktiven #define, #ifdef, #ifndef können bedingte Codeabschnitte erstellt, Konstanten deklariert und externe Dateien eingebunden werden (#include).
Geschichte der Frage:
Die Präprozessor-Direktiven wurden eingeführt, um die Anpassung des Quellcodes an verschiedene Systeme und Compiler zu erleichtern und wiederkehrende Aufgaben zu automatisieren.
Problem:
Ohne den Präprozessor konnte man sich nicht von plattformspezifischen Unterschieden abstrahieren, konnte Codefragmente nicht wiederverwenden und war nicht vor Mehrfachincluded von Header-Dateien geschützt. Außerdem muss man vorsichtig sein, da Makros typunabhängig und nicht scoping-fähig sind.
Lösung:
Die Verwendung von Präprozessor-Makros zur Deklaration von Konstanten, Inline-Funktionen, bedingter Kompilierung und zur Vermeidung mehrfacher Einschluss von Header-Dateien.
Codebeispiel:
#ifndef MY_HEADER_H #define MY_HEADER_H #define MAX_SIZE 100 #ifdef DEBUG #define LOG(x) printf("%s\n", x) #else #define LOG(x) #endif #endif /* MY_HEADER_H */
Wesentliche Merkmale:
Was passiert, wenn man den Include-Guard in einer Header-Datei vergisst?
Die Header-Datei kann mehrmals in eine .c-Datei inkludiert werden (implizit über andere .h), was zu Fehlern der Neudefinition führt.
Was unterscheidet ein #define-Makro von der Deklaration einer Inline-Funktion?
#define ersetzt einfach den Text, ohne Typen und Syntaxkorrektheit zu prüfen, während eine Inline-Funktion eine normale Funktion ist, die vom Compiler auf Typanalyse überprüft wird.
Können Makros Nebenwirkungen haben?
Ja, wenn das Makro unsachgemäß definiert ist, können übergebene Ausdrücke mehrfach ausgewertet werden. Zum Beispiel:
#define SQUARE(x) (x) * (x) int a = SQUARE(++i); // ++i wird ZWEIMAL ausgeführt!
Verwendung eines Makros ohne Klammern und mit einem Ausdruck, der Nebenwirkungen erzeugt:
#define DOUBLE(x) x + x int result = DOUBLE(1+2); // Das Ergebnis ist nicht 6, sondern 1+2+1+2=6? Nein, aber 1+2+1+2=6 — richtig? Nein, es ergibt 1+2+1+2. // In Wirklichkeit wird es 1 + 2 + 1 + 2 = 6 sein, aber wenn DOUBLE(++i) verwendet wird, wird ++i zweimal angewendet.
Vorteile:
Nachteile:
Definition eines Makros mit Klammern und Verwendung für einfache Konstanten:
#define DOUBLE(x) ((x) + (x)) #define BUFFER_SIZE 1024
Vorteile:
Nachteile: