# и ## в макросах

  #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;
  }

Просто взглянув на программу, можно "ожидать", что выход будет таким же, как для операторов printf. Но при запуске программы вы получаете ее как:

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

Почему это так?

Ответ 1

Так как работает препроцессор.

Один '#' создаст строку из данного аргумента, независимо от того, что содержит этот аргумент, в то время как double '##' создаст новый токен, объединив аргументы.

Попробуйте просмотреть предварительно обработанный вывод (например, с помощью gcc -E), если вы хотите лучше понять, как оцениваются макросы.

Ответ 2

Включение параметра в функционально-подобный макрос, если оно не является операндом # или ##, разворачивается перед заменой его и повторным сканированием целого для дальнейшего расширения. Поскольку параметр g является операндом #, аргумент не разворачивается, а сразу же стягивается ("f(1,2)"). Поскольку параметр h не является операндом # и ##, аргумент сначала расширяется (12), затем заменяется (g(12)), затем происходит повторное сканирование и дальнейшее расширение ("12").

Ответ 3

Ниже приведены некоторые связанные понятия с вашим вопросом:

Аргумент Prescan:

Макро-аргументы полностью расширены макросом до, они заменяется на макрообъект, если они не являются строковыми или вставляемымис другими жетонами. После замещения все тело макроса, включая замещенные аргументы, снова сканируется для макросов. расширен. В результате аргументы дважды проверяются, чтобы развернуть макросы в них.

Stringification

Когда параметр макроса используется с ведущим "#", препроцессор заменяет его буквальным текстом фактического аргумента, преобразуется в строковая константа.

Сочетание маркеров/Токена:

Часто бывает полезно объединить два токена в один, расширяя макросы. Это называется прикреплением маркера или конкатенацией токенов. "## оператор предварительной обработки выполняет вставку маркера. Когда макрос расширенные, два токена по обе стороны от каждого оператора ## являются объединенный в один токен, который затем заменяет" ## и два оригинальные жетоны в расширении макроса.

Итак, подробный процесс вашего сценария выглядит следующим образом:

h(f(1,2))
-> h(12) // f(1,2) pre-expanded since there 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.