О ## препроцессоре в 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
. Это побуждает меня размышлять, как компилятор интерпретирует это.