#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;
}
En regardant simplement le programme, on "pourrait" s'attendre à ce que la sortie soit la même pour les deux instructions printf. Mais en exécutant le programme, vous l'obtenez comme:
bash$ ./a.out
12
f(1,2)
bash$
Pourquoi en est-il ainsi?
Parce que c'est ainsi que fonctionne le préprocesseur.
Un simple "#" créera une chaîne à partir de l'argument donné, quel que soit son contenu, tandis que le double "##" créera un nouveau jeton en concaténant les arguments.
Essayez de regarder la sortie prétraitée (par exemple avec gcc -E
) si vous voulez mieux comprendre comment les macros sont évaluées.
Une occurrence d'un paramètre dans une macro de type fonction, sauf s'il s'agit de l'opérande de #
Ou ##
, Est développée avant de le remplacer et de réanalyser le tout pour une expansion supplémentaire. Étant donné que le paramètre de g
is l'opérande de #
, L'argument n'est pas développé mais à la place immédiatement stratifié ("f(1,2)"
). Étant donné que le paramètre de h
n'est pas l'opérande de #
Ni ##
, L'argument est d'abord développé (12
), Puis remplacé (g(12)
), puis une nouvelle numérisation et une nouvelle expansion se produisent ("12"
).
Voici quelques concepts liés à votre question:
Les arguments de macro sont complètement macro-développés avant ils sont substitués dans un corps de macro, sauf s'ils le sont stringified ou collé avec d'autres jetons. Après substitution, le corps entier de la macro, y compris les arguments substitués, est analysé encore pour les macros à développer. Le résultat est que les arguments sont analysés deux fois pour y développer les appels de macro.
Lorsqu'un paramètre de macro est utilisé avec un '#' de tête, le préprocesseur le remplace par le texte littéral de l'argument réel, converti en constante de chaîne.
#ABC => "ABC"
<---- Notez le guillemet double joint, qui est ajouté par le processus de stringification.
Collage de jetons/Concaténation de jetons :
Il est souvent utile de fusionner deux jetons en un en développant les macros. Cela s'appelle collage de jetons ou concaténation de jetons. 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 ensuite le "##" et les deux jetons d'origine dans l'extension de macro.
Le processus détaillé de votre scénario est donc le suivant:
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.