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-13.3.0/cpp/Argument-Prescan.html