web-dev-qa-db-fra.com

# et ## dans les macros

  #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?

34
algo-geeks

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.

31
Christoffer

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 gis 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 hn'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").

27
Joe Bloggs

Voici quelques concepts liés à votre question:

Argument Prescan :

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.

Stringification

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.
21
smwikipedia