2010-11-12 21 views
13

Ecco un esempio pertinente. Ovviamente non è valido C, ma mi sto occupando solo del preprocessore qui, quindi il codice in realtà non deve essere compilato.Durante l'espansione della macro C, esiste un caso speciale per macro che si espandono in "/ *"?

#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) 

Esecuzione preprocessore di GCC su di esso:

gcc -std=c99 -E macrotest.c 

Questo produce:

(...) 

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

Si prega di notare lo spazio extra nell'ultima riga.

Sembra una funzionalità che impedisce ai macro di espandersi a "/ *", che sono sicuro sia ben intenzionato. Ma a colpo d'occhio, non sono riuscito a trovare nulla che riguardasse questo comportamento nello standard C99. Poi di nuovo, sono inesperto allo C. Qualcuno può fare luce su questo? Dove è specificato? Direi che un compilatore che aderisce al C99 non dovrebbe semplicemente inserire spazi extra durante l'espansione delle macro solo perché probabilmente impedirebbe errori di programmazione.

risposta

15

Il codice sorgente è già tokenizzato prima di essere elaborato da CPP.

Quindi quello che hai è un e un gettone /* che non sarà abbinato implicitamente ad un /* "token" (dal momento che/* non è in realtà un segno preprocessore ho messo in "").

Se si utilizza -E per emettere CPP di origine preelaborata, è necessario inserire uno spazio per evitare che /* venga letto da un successivo passaggio del compilatore.

La stessa funzione previene da due, ad es. + segni da diverse macro combinate in un token ++ in uscita.

L'unico modo per incollare davvero due pedine preprocessore insieme è con l'operatore ##:

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

... 

P(foo,bar) 

risultati nel token foobar

P(+,+) 

risultati nel token ++, ma

P(/,*)  

non valido dal /* non è un token di preprocessore valido.

+0

+1. Penso che l'intuizione chiave sia che l'output di -E infatti non è specificato dallo standard.Lo standard parla del programma costituito da una sequenza di token di preelaborazione, e successivamente viene convertito in una sequenza di token. Dipende interamente dal preprocessore come rappresentare quelle sequenze, e in questo caso come serializzarle su un file come una sequenza di * byte *. Ovviamente l'unica serializzazione valida è quella che verrà riletta come una serie equivalente di token di preelaborazione, così come dici tu, deve mettere uno spazio bianco tra due token che insieme formerebbero uno. –

+0

Sono d'accordo al 100%, volevo scrivere qualcosa come la tua spiegazione ma non avevo dedicato tempo. –

+4

Buona risposta, anche se ho due commenti nitidi: non esiste un token '/ *'; i commenti vengono rimossi dalla fonte prima di essere tokenizzati. Si potrebbe formare un token '++' da due token '+' usando '##'. –

5

Il comportamento del pre-processore è standardizzato. Nel sommario allo http://en.wikipedia.org/wiki/C_preprocessor, i risultati che si stanno osservando sono l'effetto di:

"3: Tokenizzazione: il preprocessore interrompe il risultato in token e spazi bianchi di pre-elaborazione, sostituisce i commenti con spazi bianchi".

Questo avviene prima:

"4: Macro di espansione e la direttiva Handling".