2010-12-06 40 views
14
#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, uno "podría" esperar que la salida sea, lo mismo para ambas instrucciones printf. Pero al ejecutar el programa lo obtiene como:# y ## en macros

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

¿Por qué es así?

Respuesta

18

Porque así es como funciona el preprocesador.

Un único '#' creará una cadena a partir del argumento dado, independientemente de lo que contenga ese argumento, 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.

13

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. Como el parámetro ges el operando de #, el argumento no se expande, sino que se codifica inmediatamente ("f(1,2)"). Debido h 's parámetro no es el operando de # ni ##, se expande primero el argumento (12), a continuación, sustituida (g(12)), a continuación, volver a analizar y se produce una mayor expansión ("12").

4

A continuación se presentan algunos conceptos relacionados con su pregunta:

Argument Prescan:

argumentos de macro son completamente macro-expandida antes son sustituido en un cuerpo de la macro, a menos que sean stringified o pegado con otros tokens. Después de la sustitución, todo el cuerpo de la macro, incluyendo los argumentos sustituidos, se escanea nuevamente para que las macros sean expandidas. El resultado es que los argumentos se escanean dos veces para expandir las llamadas de macro .

Stringification

Cuando un parámetro de macro se utiliza con un '#' líder, el preprocesador la sustituye por el texto literal del argumento actual, se convirtieron al una constante cadena .

Token Pasting/Token Concatenation:

A menudo es útil para combinar dos fichas en uno mientras se expande macros. Esto se llama token pegando o concatenación de token. El operador de preprocesamiento '##' realiza el pegado de tokens. Cuando una macro es expandida, los dos tokens a cada lado de cada operador '##' son combinados en un solo token, que luego reemplaza los tokens originales '##' y los dos tokens originales en la expansión de macros.

lo tanto, el proceso detallado de su situación es la siguiente:

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.