О ## препроцессоре в C

Учитывая

#define cat(x,y) x##y

Вызов cat(a,1) возвращает a1, но cat(cat(1,2),3) - undefined. Однако, если я также определяю #define xcat(x,y) cat(x,y), тогда результат xcat(xcat(1,2),3) теперь 123. Кто-нибудь может объяснить, почему это так?

Ответ 1

Я тестировал это, используя как GCC, так и Clang.

GCC дает ошибку:

test.c:6:1: error: pasting ")" and "3" does not give a valid preprocessing token

Clang дает ошибку:

test.c:6:11: error: pasting formed ')3', an invalid preprocessing token
  int b = cat(cat(1,2),3);

То, что, похоже, происходит, заключается в том, что компилятор завершает результат cat(1,2) в круглых скобках, как только он расширяется; поэтому, когда вы вызываете cat(1,2) в свой код, он действительно дает вам (12). Затем вызов cat((12),3) снова приводит к ((12)3), который не является допустимым токеном, и это приводит к ошибке компиляции.

Общим мнением является "при использовании оператора маркировки (##), вы должны использовать два уровня косвенности" (т.е. использовать обходное решение xcat). См. Почему мне нужен двойной слой косвенности для макросов? и Что делать с макросами, которые нужно вставить два токена вместе?.

Ответ 2

В xcat (x, y) x и y не смежны с оператором ## и поэтому перед заменой они подвергаются макрорасширению.

Итак, x идентифицируется как xcat (1,2), а y идентифицируется как 3. Но ранее к замене, x макрорасширен до cat (1,2), который превращается в 1 ## 2 который превращается в 12. Таким образом, в конечном счете, xcat (xcat (1,2), 3) будет расширяться к cat (12,3), который получится 123.

Это работает → cat (xcat (1,2), 3) → cat (cat (1,2), 3) → cat (12,3)

Поведение четко определено, потому что все точечные приставки приведут к действительным токенам препроцессора. Любое расширенное xpression должно быть допустимым токеном на любом этапе.

Ответ 3

Я не думаю, что если кошка действительно будет разворачиваться 2 раза подряд. Поэтому я задаюсь вопросом, почему компилятор даже создаст такое сообщение, как 'pasting ")" and "3" does not give a valid preprocessing token'. Кроме того, я не думаю, что внутренняя кошка будет расширена в первую очередь. Итак, я предполагаю, что выход будет cat(1,2)3. Это побуждает меня размышлять, как компилятор интерпретирует это.