2014-12-17 4 views
5

Ecco un semplice programma di po C che mi aveva confuso per un po ':Evitare una doppia possibilità di sostituzione macro in C pre-processore

#include <stdio.h> 
#define STR1(x) #x 
#define STR(x) STR1(x) 
int main(void) { 
    printf("%s\n", STR(MYDEF)); 
} 

viene stampato solo il valore del #define MYDEF come una stringa, utilizzando la tecnica di defi- nizione a doppia definizione standard.

Compile (su Linux) con gcc -DMYDEF=abc prog.c esegue il risultato e, non sorprendentemente, stampa "abc".

Ma cambiare il valore gcc -DMYDEF=linux prog.c e il risultato stampato non è 'linux' ma '1'.

Quindi questo mi ha confuso un po ', ma ovviamente succede perché gcc (su Linux) ha, ho scoperto, un #define incorporato per il nome' linux 'con un valore' 1 ', e il STR (x) la macro finisce espandendo MYDEF in 'linux', quindi linux in '1'.

Nel mio vero programma (che era piuttosto più complesso del piccolo test di cui sopra) ho aggirato questo facendo cose in un modo diverso (probabilmente migliore), ma mi ha lasciato curioso ... c'è una piccola macro semplice tecnica che eviterebbe questa doppia sostituzione e farà stampare il programma 'linux'? So che potrei aggiungere un -U o #undef di linux, ma questo mi sembra un po 'goffo.

Avevo pensato che tutti i #defines incorporati iniziassero con caratteri di sottolineatura (di solito doppi caratteri di sottolineatura), ma immagino di no.

+1

Mi sembra che tu abbia elencato tutte le risposte. – 2501

+0

tranne uno - evitare le macro –

+0

Aggiungere '-Dlinux = linux'. - Questo interromperà il codice che si basa su '#if linux' o simili, ma non romperà il codice che si basa su' #if defined (linux) 'o simili. –

risposta

1

Non v'è alcun modo per espandere una macro solo una volta, c'è sempre un performante nuova scansione ulteriore sostituzione (mai ricorsiva, ovviamente). Vi sono circostanze in cui le macro non vengono espanse affatto (come con l'operatore #), motivo per cui è necessario il livello di sostituzione aggiuntivo con due #define come nell'esempio.

In ISO C, gli identificatori senza un trattino di sottolineatura principale sono gratuiti per l'utilizzo (non tutti, per la precisione). I dialetti GNU C definiscono alcune altre macro per impostazione predefinita (come linux) per compatibilità con le versioni precedenti, anche se prevedono di rimuovere tali macro in futuro.

per ottenere un elenco di tali macro sul vostro computer, si può fare:

$ echo | gcc -std=gnu99 -E -dM - | grep -v '# *define *_' 
#define unix 1 
#define linux 1 
#define i386 1 

con le opzioni per ISO C (-ansi/-std=c89, -std=c99, -std=c11/-std=c1x per Gcc più anziani), queste macro sono non definiti:

$ cat test.c  
#define STR1(x) #x 
#define STR(x) STR1(x) 
STR(MYDEF); 
STR1(MYDEF); 
$ gcc -std=gnu99 -DMYDEF=linux -E test.c 
# 1 "test.c" 
# 1 "<command-line>" 
# 1 "test.c" 


"1"; 
"MYDEF"; 
$ gcc -std=c99 -DMYDEF=linux -E test.c 
# 1 "test.c" 
# 1 "<command-line>" 
# 1 "test.c" 


"linux"; 
"MYDEF"; 

in modalità ISO C, queste macro sono correttamente nello spazio dei nomi riservati:

$ echo | gcc -std=c99 -E -dM - | grep linux 
#define __linux 1 
#define __linux__ 1 
#define __gnu_linux__ 1 
1

vedo nel manuale di gcc che è possibile utilizzare l'opzione -ansi per spegnere macro predefinite come "linux"

gcc -ansi -DMYDEF=linux prog.c