2010-12-06 7 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; 
    } 

Juste en regardant le programme on pourrait "attendre" que la sortie soit la même pour les deux instructions printf. Mais en exécutant le programme, vous obtenez comme:# et ## dans les macros

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

Pourquoi est-ce vrai?

Répondre

18

Parce que c'est ainsi que fonctionne le préprocesseur. Un simple '#' créera une chaîne à partir de l'argument donné, indépendamment de ce que cet argument contient, tandis que le double '##' créera un nouveau jeton en concaténant les arguments. Essayez de regarder la sortie prétraite (par exemple avec gcc -E) si vous voulez mieux comprendre comment les macros sont évaluées.

13

Une occurrence d'un paramètre dans une macro de type fonction, sauf s'il s'agit de l'opérande # ou ##, est développée avant d'être remplacée et de réanalyser l'ensemble pour une expansion ultérieure. Parce que g le paramètre est l'opérande de #, l'argument n'est pas développé mais au lieu immédiatement stringifié ("f(1,2)"). Étant donné que le paramètre de hne l'opérande de # ni ##, l'argument est d'abord étendu (12), puis substitué (g(12)), puis le rebalayage et l'expansion se produit ("12").

4

Voici quelques concepts liés à votre question:

Argument Prescan:

arguments macro sont complètement macro-expansion avant ils sont substitués dans un corps macro, à moins qu'ils ne sont stringifié ou collé avec d'autres jetons. Après substitution, le corps de la macro entière, comprenant les arguments substitués, est analysé à nouveau pour que les macros soient développées. Le résultat est que les arguments sont analysés deux fois pour développer appels de macro dans eux.

Stringification

Lorsqu'un paramètre macro est utilisé avec l'un des principaux « # », le préprocesseur le remplace par le texte littéral de l'argument réel, converti en une constante chaîne .

Token Pasting/Token Concatenation:

Il est souvent utile de fusionner deux jetons en un tout en développant macros. Ceci est appelé jeton de collage ou concaténation de jeton. L'opérateur de prétraitement '##' effectue le collage de jetons. Lorsqu'une macro est développée, les deux jetons de chaque côté de chaque opérateur '##' sont combinés en un seul jeton, qui remplace alors le '##' et les deux jetons d'origine dans le développement de macro.

Ainsi, le processus détaillé de votre scénario est comme ceci:

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.