2015-06-12 9 views
7

ich auf CPP Makroerweiterung lese und wollte Expansion verstehen, wenn der (optional) Token-String ist nicht vorgesehen. Ich fand gcc v4.8.4 dies tut:cpp Erweiterung des Makro ohne Token-String

$ cat zz.c 
#define B 
(B) 
|B| 
$ gcc -E zz.c 
# 1 "zz.c" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "zz.c" 

() 
| | 

Kann mir jemand erklären, warum die Erweiterung Null Räume in einem Fall ist und eine in der anderen?

+1

Wahrscheinlich, weil '|' und '' || sind verschiedene Betreiber. Ich hoffe jemand schreibt eine Antwort mit genauen Regeln die cpp benutzt. – hyde

+0

Es kann lustig sein, jemanden zu sehen, der 'x | schreibt Wert | SOME_FLAG' und es stellt sich heraus, dass es 'x || ist SOME_FLAG', ist es wahrscheinlich so, also wird es nicht kompilieren. –

+0

Das würde aber bedeuten, dass '||' als Sonderfall behandelt wird. Dies könnte vermieden werden, indem * immer * Leerzeichen eingefügt werden, die nicht gegen C-Regeln stehen (mit Ausnahme von Makroverkettungen). – usr2564301

Antwort

7

Der C-Präprozessor arbeitet mit "Token" und wenn immer die Möglichkeit besteht, die Bedeutung oder Ambiguität zu ändern, fügt er immer Leerzeichen hinzu, um die Bedeutung zu bewahren.

Ihr Beispiel Betrachten sie

(B) 

gibt es keine Zweideutigkeit oder Sinn zu verändern, ob es einen Raum zwischen ( und ) hinzugefügt oder nicht, unabhängig von dem Makrowert von B.

Aber es ist nicht der Fall mit

|B| 

auf der Makro B Je dies oben könnte entweder || oder |something| sein. So wird Präprozessor gezwungen, ein Leerzeichen hinzuzufügen, um C der lexikalischen Regeln zu halten.

Das gleiche Verhalten kann mit anderen Token zu erkennen, dass die Bedeutung verändern könnte.Zum Beispiel würde

#define B + 
B+ 

+ + 

produzieren im Gegensatz zu

++ 

für den genannten Grund.

Dies ist jedoch nur der Präprozessor, der den C lexikalischen Regeln entspricht. GCC hat und unterstützt einen alten Präprozessor namens traditionellen Prozessor, die keine zusätzlichen Leerzeichen hinzufügen würde. wenn Sie Präprozessor in traditionellen Modus Zum Beispiel rufen:

gcc -E -traditional-cpp file.c 

dann

#define B 

(B) 
|B| 

produzieren (ohne Leerzeichen)

() 
|| 
1

edit: hvd Antwort über die gcc Präprozessor Implementierung

Dies kann zwischen den bitweise und logischen OR-Operatoren sehen unterscheiden.

Dieses Beispiel:

if (x | 4) printf("true\n"); // Bitwise OR, may or may not be true 

unterscheidet sich von:

if (x || 4) printf("true\n"); // Always true 

Da sie verschiedene Operatoren mit unterschiedlichen Funktionen sind, ist es notwendig, für die Prä-Prozessor Leerzeichen hinzufügen Ändern der beabsichtigten Bedeutung zu vermeiden die Aussage.

+2

Es beantwortet nicht die Frage: Warum ist es getan? es hätte nicht an erster Stelle gemacht werden können. –

4

Die Ausgabe von gcc -E nicht absichtlich nicht die genauen Regeln, die von dem C-Standard spezifizierten entsprechen. Der C-Standard beschreiben keine besondere Art und Weise der Präprozessor Ergebnis sichtbar sein sollte, und nicht einmal eine solche Art und Weise erfordern existieren.

Die einzige Zeit, zu der eine Art von Präprozessorausgabe sichtbar sein muss, ist, wenn der Operator verwendet wird. Und wenn Sie diese verwenden, können Sie sehen, dass es keinen Raum gibt.

flaming.toaster Antwort zu Recht darauf hin, dass der Grund der gcc -E Ausgabe einen Raum einfügt ist, die zwei aufeinanderfolgende | s um zu verhindern, als ein einziges || Token analysiert werden. Das folgende Programm wird benötigt, um eine Diagnose für die Syntaxfehler zu geben:

#define EMPTY 
int main() { return 0 |EMPTY| 0; } 

und der Raum ist es um sicherzustellen, dass der Compiler noch genügend Informationen, um tatsächlich zu erzeugen, um den Fehler aufweist.

+0

Also werden Dateien nach Operatoren mit einem Makro dazwischen gescannt, was zu einem * einzigen * Operator führen würde, wenn er leer ist? (Eine kurze Liste: '++', '-', '->', und die booleschen Operatoren. Noch mehr?) – usr2564301

+0

@Jongware Betrachte auch zwei aufeinanderfolgende Bezeichner: '#define F (X) X', und dann 'F (int) F (main)() {}'. Tatsächlich besteht der Ansatz von GCC darin, dass zwei aufeinanderfolgende Tokentypen einen Platz zwischen ihnen erhalten, wenn einige Token dieser Typen fehlinterpretiert würden, wenn sie links daneben liegen, selbst wenn dies kein Problem für die fraglichen Token ist Platz in einigen Eckfällen, wo es nicht unbedingt benötigt wird, zum Beispiel zwischen "1" und "+", da "+" * * in einer pp-Nummer erscheinen kann (nach einem "E"). – hvd

+0

Gibt es einen guten Grund, nicht immer * immer * ein Leerzeichen vor und nach einem erweiterten Makro einzufügen? Das scheint mir die einfachste Lösung zu sein ... – usr2564301