Cercando di capire il preprocessore C
-
17-09-2020 - |
Domanda
Perché questi blocchi di codice producono diversi risultati?
Qualche codice comune:
#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)
.
Caso 1:
#define GLUE(a,b,c) a##b##c
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"
.
Caso 2:
#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"
.
Caso 3:
#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"
.
Sto usando MSVC ++ da VS.NET 2005 SP1
Modifica: Attualmente è la mia convinzione che il preprocessore funzioni in questo modo quando si espande le macro: Passo 1: - Prendi il corpo - Rimuovere qualsiasi spazio bianco attorno agli operatori ## - Analizza la stringa, nel caso in cui venga trovato un identificatore che corrisponde al nome di un parametro: -Se è vicino a un operatore ##, sostituire l'identificatore con il valore letterale del parametro (cioè la stringa passata) -Se non è accanto a un operatore ##, eseguire questo intero processo di spiegazione sul valore del parametro in primo luogo, quindi sostituire l'identificatore con quel risultato. (ignorando il custodia SingleFy Single "#" ATM) -Rimuovere tutti i ## operatori
Step 2: - Prendi quella stringa risultante e analizzalo per qualsiasi macro
Ora, da quello credo che tutti e 3 i casi dovrebbero produrre la stessa identica stringa risultante:
part1part2 *
E quindi dopo il passaggio 2, dovrebbe provocare
Lavori *
Ma almeno dovrebbe causare la stessa cosa.
Soluzione
Casi 1 e 2 non hanno alcun comportamento definito poiché si tentano di incollare un *
in un token preprocessore. Secondo le regole di associazione del preprocessore, si tenta di colla insieme dei token PART1PART2
(o solo PART2
) e *
. Nel tuo caso, questo probabilmente fallisce in silenzio, il che è uno dei possibili risultati quando le cose non sono definite. Il token PART1PART2
seguito da *
non sarà quindi considerato per la macro espansione. Stringa è quindi produce il risultato che vedi.
Il mio GCC si comporta in modo diverso sui tuoi esempi:
/usr/bin/gcc -O0 -g -std=c89 -pedantic -E test-prepro.c
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token
"works*"
.
Pertanto per riassumere il tuo caso 1 ha due problemi.
- .
- incollando due token che non risultano in un token preprocessore valido.
- Ordine di valutazione dell'operatore
##
Nella causa 3, il tuo compilatore sta dando il risultato sbagliato. Dovrebbe
- .
- Valuta gli argomenti a
STRINGAFY1
- per fare che deve espandere
GLUE
-
GLUE
Risultati inPART1PART2*
- che deve essere espanso di nuovo
- Il risultato è
works*
- che poi è passato a
STRINGAFY1
Altri suggerimenti
Sta facendo esattamente quello che stai dicendo di farlo.Il primo e il secondo prendere i nomi dei simboli passati e incollali insieme in un nuovo simbolo.Il terzo prende 2 simboli e li insegue, allora stai mettendo il * nella corda da soli (che alla fine valuterà in qualcos'altro).
Qual è esattamente la domanda con i risultati?Cosa ti aspettavi di ottenere?Tutto sembra funzionare come mi aspetterei.
Allora ovviamente è la domanda del perché stai giocando con le arti scure del simbolo comunque in questo modo?:)