# e ## in macro

#include  #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { printf("%s\n",h(f(1,2))); printf("%s\n",g(f(1,2))); return 0; } 

Solo guardando il programma uno “potrebbe” aspettarsi che l’output sia, lo stesso per entrambe le affermazioni printf. Ma durante l’esecuzione del programma si ottiene come:

 bash$ ./a.out 12 f(1,2) bash$ 

Perché è così?

Perché è così che funziona il preprocessore.

Un singolo ‘#’ creerà una stringa dall’argomento dato, indipendentemente da cosa contenga quell’argomento, mentre il doppio ‘##’ creerà un nuovo token concatenando gli argomenti.

Prova a guardare l’output preelaborato (ad esempio con gcc -E ) se vuoi capire meglio come vengono valutate le macro.

Un’occorrenza di un parametro in una macro simile a una funzione, a meno che non si tratti dell’operando di # o ## , viene espansa prima di sostituirla e riesaminare il tutto per un’ulteriore espansione. Poiché il parametro di g è l’operando di # , l’argomento non viene espanso ma invece immediatamente stringa ( "f(1,2)" ). Poiché il parametro di h non è l’operando di ### , l’argomento viene prima espanso ( 12 ), quindi sostituito ( g(12) ), quindi viene eseguita nuovamente la scansione e si verifica un’ulteriore espansione ( "12" ).

Di seguito sono riportati alcuni concetti correlati alla tua domanda:

Argument Prescan :

Gli argomenti macro sono completamente macro-espansi prima di essere sostituiti in un corpo macro, a meno che non siano stati uniti o incollati con altri token. Dopo la sostituzione, l’intero corpo della macro, inclusi gli argomenti sostituiti, viene nuovamente scansionato per espandere i macro. Il risultato è che gli argomenti vengono scansionati due volte per espandere le chiamate macro in essi.

in stringa

Quando un parametro macro viene utilizzato con un “#” iniziale, il preprocessore lo sostituisce con il testo letterale dell’argomento effettivo, convertito in costante di stringa .

Concatenazione token / token :

È spesso utile unire due token in uno mentre si espandono i macro. Questo è chiamato token pasting o token concatenation . L’operatore di preelaborazione “##” esegue l’incollatura dei token. Quando una macro viene espansa, i due token su ciascun lato di ciascun operatore ‘##’ vengono combinati in un singolo token, che sostituisce quindi ‘##’ e i due token originali nell’espansione macro.

Quindi il processo dettagliato del tuo scenario è come questo:

 h(f(1,2)) -> h(12) // f(1,2) pre-expanded since there's no # or ## in macro h -> g(12) // h expanded to g 12 // g expanded g(f(1,2)) -> "f(1,2)" //f(1,2) is literally strigified because of the `#` in macro g. f(1,2) is NOT expanded at all.