2010-11-12 2 views
13

Hier ist ein relevantes Beispiel. Es ist offensichtlich nicht gültig C, aber ich habe hier nur mit dem Präprozessor zu tun, also muss der Code nicht wirklich kompilieren.Gibt es während der C-Makro-Erweiterung einen Sonderfall für Makros, die auf "/ *" erweitert werden?

#define IDENTITY(x) x 
#define PREPEND_ASTERISK(x) *x 
#define PREPEND_SLASH(x) /x 

IDENTITY(literal) 
PREPEND_ASTERISK(literal) 
PREPEND_SLASH(literal) 
IDENTITY(*pointer) 
PREPEND_ASTERISK(*pointer) 
PREPEND_SLASH(*pointer) 

Lauf gcc Präprozessor darauf:

gcc -std=c99 -E macrotest.c 

Dies ergibt:

(...) 

literal 
*literal 
/literal 
*pointer 
**pointer 
/*pointer 

Bitte beachten Sie den zusätzlichen Platz in der letzten Zeile.

Das sieht wie ein Feature aus, um zu verhindern, dass Makros auf "/ *" zu mir expandieren, was ich mir sicher gut gemacht habe. Aber auf einen Blick konnte ich im C99-Standard nichts zu diesem Verhalten finden. Andererseits bin ich unerfahren bei C. Kann jemand etwas Licht darauf werfen? Wo ist das angegeben? Ich würde vermuten, dass ein Compiler, der an C99 anknüpft, nicht einfach zusätzliche Leerzeichen während der Makroerweiterung einfügen sollte, nur weil es wahrscheinlich Programmierfehler verhindern würde.

Antwort

15

Der Quellcode wurde bereits vor der Verarbeitung durch CPP in Token zerlegt.

Also, was Sie haben, ist ein / und ein * Token, die kombiniert werden nicht implizit in einen /* „Token“ (da/* ist nicht wirklich ein Präprozessor-Token ich es in „“).

Wenn Sie -E verwenden, um vorverarbeitete Quell-CPP auszugeben, muss ein Leerzeichen eingefügt werden, um zu vermeiden, dass /* von einem nachfolgenden Compiler-Durchlauf gelesen wird.

Das gleiche Merkmal verhindert von zwei z.B. + Zeichen aus verschiedenen Makros werden in einem ++ Token am Ausgang kombiniert.

Der einzige Weg, wirklich fügen zwei Präprozessor zusammen mit den ## Operator-Token ist:

#define P(x,y) x##y 

... 

P(foo,bar) 

Ergebnisse in den Token foobar

P(+,+) 

Ergebnisse im Token ++, aber

P(/,*)  

ist nicht gültig seit /* ist kein gültiges Präprozessor-Token.

+0

+1. Ich denke, die wichtigste Erkenntnis ist, dass die Ausgabe von -E in der Tat nicht durch den Standard spezifiziert ist.Der Standard spricht über das Programm, das aus einer Folge von Vorverarbeitungstoken besteht, und später wird es in eine Folge von Token umgewandelt. Es hängt vollständig vom Präprozessor ab, wie diese Sequenzen dargestellt werden, und in diesem Fall, wie man sie als eine Folge von * Bytes * in eine Datei serialisiert. Natürlich ist die einzige brauchbare Serialisierung eine, die als äquivalente Reihe von Vorverarbeitungstoken zurückgelesen wird, so wie Sie sagen, sie muss Leerzeichen zwischen zwei Token setzen, die zusammen eins bilden. –

+0

Ich stimme 100% zu, wollte etwas wie deine Erklärung schreiben, hatte aber keine Zeit. –

+4

Gute Antwort, obwohl ich zwei nitpicky Kommentare habe: Es gibt nicht so etwas wie ein '/ *' Token; Kommentare werden aus der Quelle entfernt, bevor sie in Token umgewandelt werden. Sie könnten ein '++' Token aus zwei '+' Tokens mit '##' bilden. –

5

Das Verhalten des Vorprozessors ist standardisiert. In der Zusammenfassung unter http://en.wikipedia.org/wiki/C_preprocessor sind die Ergebnisse, die Sie beobachten, die Auswirkungen von:

"3: Tokenization - Der Präprozessor bricht das Ergebnis in Vorverarbeitungstoken und Leerzeichen. Es ersetzt Kommentare mit Leerzeichen".

Dies geschieht vor:

"4: Makroerweiterung und die Richtlinie Handling".

Verwandte Themen