2015-06-12 29 views
7

Sto leggendo sull'espansione macro CPP e volevo capire l'espansione quando non è fornita la token-stringa (facoltativa). Ho trovato v4.8.4 gcc fa questo:Espansione cpp di macro senza 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" 

() 
| | 

qualcuno può spiegare il motivo per cui l'espansione è pari a zero spazi in un caso e uno nell'altro?

+1

Probabilmente perché '|' e '' || sono diversi operatori. Spero che qualcuno scriva una risposta con le regole esatte utilizzate da cpp. – hyde

+0

Può essere divertente vedere qualcuno scrivere 'x | valore | SOME_FLAG' e risulta essere 'x || SOME_FLAG', probabilmente è così che non verrà compilato. –

+0

Ma ciò significherebbe che '||' viene trattato come un caso speciale. Questo potrebbe essere evitato * inserendo * sempre spazi, che non sarebbero contro le regole C (eccetto per le concatenazioni macro). – usr2564301

risposta

7

Il preprocessore C opera su "token" e ogni volta che è possibile modificare il significato o l'ambiguità, aggiunge sempre spazi bianchi per preservare il significato.

consideri il tuo esempio,

(B) 

non c'è alcuna ambiguità o significato alterare se c'è uno spazio tra ( e ) aggiunto o meno indipendentemente dal valore della macro B.

Ma non è il caso di

|B| 

seconda della macro B, questo di cui sopra potrebbe essere sia || o |something|. Quindi il preprocessore è costretto ad aggiungere uno spazio bianco per mantenere le regole lessicali di C.

Lo stesso comportamento può essere visto con qualsiasi altro token che potrebbe alterare il significato.Ad esempio,

#define B + 
B+ 

produrrebbe

+ + 

anziché

++ 

per detto motivo.

Tuttavia, questo è solo il preprocessore conforme alle regole lessicali C. GCC ha e supporta un vecchio preprocessore chiamato processore tradizionale che non aggiungerebbe alcun spazio aggiuntivo. Ad esempio, se si chiama preprocessore in modalità tradizionale:

gcc -E -traditional-cpp file.c 

poi

#define B 

(B) 
|B| 

prodotti (senza gli spazi)

() 
|| 
1

edit: vedi la risposta di hvd sull'implementazione del preprocessore di gcc

Questo può essere quello di distinguere tra il bit a bit e logica OR operatori.

Questo campione:

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

è diverso da:

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

Dal momento che sono diversi operatori con funzioni diverse, è necessario che il preprocessore per aggiungere spazio bianco per evitare di modificare il significato inteso di la dichiarazione.

+2

Non risponde alla domanda: perché è fatto? non avrebbe potuto essere fatto in primo luogo. –

4

L'output di gcc -E intenzionalmente non corrisponde alle regole esatte specificate dallo standard C. Lo standard C non descrive in alcun modo in cui il risultato del preprocessore dovrebbe essere visibile e non richiede nemmeno che esista.

L'unica volta che è necessario visualizzare una sorta di output del preprocessore è quando viene utilizzato l'operatore #. E se lo usi, puoi vedere che non c'è spazio. risposta

di flaming.toaster giustamente sottolinea che la ragione l'uscita gcc -E inserisce uno spazio consiste nell'impedire che i due consecutivi | s venga analizzato come singolo || token. Il seguente programma è tenuto a dare una diagnosi per l'errore di sintassi:

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

e lo spazio è lì per assicurarsi che il compilatore ha ancora abbastanza informazioni per generare in realtà l'errore.

+0

Quindi i file vengono scansiti per gli operatori con una macro in mezzo che porterebbe a un operatore * single * quando vuoto? (Una lista veloce: '++', '--',' -> ', e gli operatori booleani. Ancora di più?) – usr2564301

+0

@Jongware Consideriamo anche due identificatori consecutivi:' #define F (X) X', e quindi 'F (int) F (main)() {}'. In realtà, l'approccio di GCC è che due tipi di token consecutivi ottengono uno spazio inserito tra di loro se alcuni token di quei tipi vengano interpretati erroneamente se lasciati adiacenti, anche se non è un problema per i token specifici in questione, quindi potresti vedere un extra spazio in alcuni casi d'angolo in cui non è strettamente necessario, ad esempio tra '1' e' + ', poiché' + '* potrebbe * apparire in un numero pp (dopo un' E'). – hvd

+0

C'è una buona ragione per non solo * sempre * inserire uno spazio prima e dopo una macro espansa? Questa mi sembra la soluzione più semplice ... – usr2564301

Problemi correlati