3.10.6 Предварительный анализ аргументов ¶
Аргументы макроса полностью макрорасширяются до их подстановки в тело макроса, за исключением случаев, когда они строятся или склеиваются с другими токенами. После подстановки всё тело макроса, включая подставленные аргументы, снова сканируется на макросы для расширения. В результате аргументы сканируются дважды, чтобы расширить вызовы макросов в них.
В большинстве случаев это не оказывает никакого влияния. Если аргумент содержал вызовы макросов, они расширяются во время первого сканирования. В результате расширения не содержит вызовов макросов, поэтому второе сканирование его не изменяет. Если бы аргумент подставлялся как есть, без предварительного сканирования, единственное оставшееся сканирование обнаружило бы те же вызовы макросов и дало бы те же результаты.
Можно ожидать, что двойное сканирование изменит результаты при использовании самоссылочного макроса в аргументе другого макроса (см. Самоссылочные макросы): самоссылочный макрос был бы расширен один раз в первом сканировании и второй раз во втором сканировании. Однако этого не происходит. Самоссылочные элементы, которые не расширяются в первом сканировании, помечаются так, что они не будут расширяться и во втором сканировании.
Можно задаться вопросом: «Зачем упоминать предварительный анализ, если он не влияет на результат? И почему не пропустить его и не сделать препроцессор быстрее?» Ответ состоит в том, что предварительный анализ влияет на результат в трех особых случаях:
- Вложенные вызовы макроса.
Мы говорим, что вложенные вызовы макроса происходят, когда аргумент макроса содержит вызов того же самого макроса. Например, если
f— это макрос, ожидающий один аргумент,f (f (1))— это вложенная пара вызововf. Желаемое расширение выполняется путём расширенияf (1)и подстановки его в определениеf. Предварительный анализ приводит к ожидаемому результату. Без предварительного анализаf (1)сам бы был подставлен как аргумент, а внутреннее использованиеfпоявилось бы во время основного сканирования как косвенная самоссылка и не было бы расширено. - Макросы, которые вызывают другие макросы, которые строят или конкатенируют.
Если аргумент строится или конкатенируется, предварительный анализ не выполняется. Если нужно расширить макрос, а затем построить или конкатенировать его расширение, это можно сделать, заставив один макрос вызвать другой макрос, который выполняет строку или конкатенацию. Например, если у вас есть
#define AFTERX(x) X_ ## x #define XAFTERX(x) AFTERX(x) #define TABLESIZE 1024 #define BUFSIZE TABLESIZE
тогда
AFTERX(BUFSIZE)расширяется доX_BUFSIZE, аXAFTERX(BUFSIZE)расширяется доX_1024. (Не доX_TABLESIZE. Предварительный анализ всегда выполняет полное расширение.) - Макросы, используемые в аргументах, чьи расширения содержат незащищённые запятые.
Это может привести к тому, что макрос, расширенный во втором сканировании, будет вызван с неправильным количеством аргументов. Вот пример:
#define foo a,b #define bar(x) lose(x) #define lose(x) (1 + (x))
Мы хотели бы, чтобы
bar(foo)превратилось в(1 + (foo)), которое затем превратилось бы в(1 + (a,b)). Вместо этогоbar(foo)расширяется доlose(a,b), и вы получите ошибку, потому чтоloseтребует одного аргумента. В этом случае проблему легко решить теми же скобками, которые должны использоваться для предотвращения неправильной вложенности арифметических операций:#define foo (a,b)
or
#define bar(x) lose((x))
Дополнительная пара скобок предотвращает интерпретацию запятой в определении
fooкак разделителя аргументов.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-10.5.0/cpp/Argument-Prescan.html