# y## en macros
c-preprocessor stringification (3)
#include <stdio.h>
#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 programa, "podría" esperar que la salida sea la misma, tanto para las sentencias de printf. Pero al ejecutar el programa lo obtienes como:
bash$ ./a.out
12
f(1,2)
bash$
¿Por que es esto entonces?
A continuación se presentan algunos conceptos relacionados con su pregunta:
Los argumentos de macro se expanden completamente en macro antes de que se sustituyan en un cuerpo de macro, a menos que estén agrupados o pegados con otros tokens. Después de la sustitución, el cuerpo completo de la macro, incluidos los argumentos sustituidos, se escanea de nuevo para expandir las macros. El resultado es que los argumentos se analizan dos veces para expandir las llamadas de macro en ellos.
Cuando se usa un parámetro de macro con un ''#'' inicial, el preprocesador lo reemplaza con el texto literal del argumento real, convertido en una cadena constante .
Pegado de token / Concatenación de token :
A menudo es útil combinar dos tokens en uno mientras se expanden las macros. Esto se denomina pegado de token o concatenación de token . El operador de preprocesamiento ''##'' realiza el pegado de token. Cuando una macro se expande, los dos tokens a cada lado de cada operador ''##'' se combinan en un solo token, que luego reemplaza el ''##'' y los dos tokens originales en la expansión de la macro.
Entonces el proceso detallado de tu 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.
La aparición de un parámetro en una macro similar a una función, a menos que sea el operando de #
o ##
, se expande antes de sustituirlo y volver a explorar el conjunto para una expansión adicional. Debido a que el parámetro de g
es el operando de #
, el argumento no se expande sino que se enmarca inmediatamente ( "f(1,2)"
). Como el parámetro de h
no es el operando de #
ni ##
, el argumento primero se expande ( 12
), luego se sustituye ( g(12)
), luego se vuelve a explorar y se produce una expansión adicional ( "12"
).
Porque así es como funciona el preprocesador.
Un solo ''#'' creará una cadena a partir del argumento dado, independientemente de lo que contenga ese argumento, mientras que el doble ''##'' creará una nueva ficha concatenando los argumentos.
Intente ver el resultado preprocesado (por ejemplo, con gcc -E
) si desea comprender mejor cómo se evalúan las macros.