# y ## en macros

#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; } 

Con solo mirar el progtwig, uno “podría” esperar que la salida sea, lo mismo para ambas declaraciones printf. Pero al ejecutar el progtwig, lo obtiene como:

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

¿Por que es esto entonces?

Porque así es como funciona el preprocesador.

Un único ‘#’ creará una cadena a partir del argumento dado, independientemente de lo que ese argumento contenga, mientras que el doble ‘##’ creará un nuevo token al concatenar los argumentos.

Intente ver la salida preprocesada (por ejemplo, con gcc -E ) si desea comprender mejor cómo se evalúan las macros.

La aparición de un parámetro en una macro de tipo función, a menos que sea el operando de # o ## , se expande antes de sustituirlo y volver a explorar el todo para una mayor expansión. Debido a que el parámetro g es el operando de # , el argumento no se expande, sino que se corrige inmediatamente ( "f(1,2)" ). Como el parámetro h no es el operando de # ni ## , el argumento primero se expande ( 12 ), luego se sustituye ( g(12) ), luego se vuelve a escanear y se produce una expansión adicional ( "12" ).

A continuación hay algunos conceptos relacionados con su pregunta:

Argumento Prescan :

Los argumentos de macro se expanden completamente en macro antes de ser sustituidos en un macro cuerpo, a menos que estén codificados o pegados con otros tokens. Después de la sustitución, todo el macro cuerpo, incluidos los argumentos sustituidos, se escanea de nuevo para que las macros se expandan. El resultado es que los argumentos se escanean dos veces para expandir las llamadas de macro en ellos.

Stringificación

Cuando se utiliza un parámetro de macro con un ‘#’ inicial, el preprocesador lo reemplaza con el texto literal del argumento real, convertido a una constante de cadena .

Token Pasting / Token Concatenation :

A menudo es útil fusionar dos tokens en uno mientras se expanden las macros. Esto se llama pegar token o concatenación de token . El operador de preprocesamiento ‘##’ realiza el pegado de tokens. Cuando se expande una macro, los dos tokens a cada lado de cada operador ‘##’ se combinan en un único token, que luego reemplaza el ‘##’ y los dos tokens originales en la expansión de macro.

Entonces el proceso detallado de su escenario es así:

 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.