3.3 Аргументы макросов
Макросы, похожие на функции, могут принимать аргументы, как и настоящие функции. Чтобы определить макрос, использующий аргументы, введите параметры между скобками в определении макроса, делая макрос похожим на функцию. Параметры должны быть допустимыми идентификаторами C, разделенными запятыми и, необязательно, пробелами.
Для вызова макроса, принимающего аргументы, запишите имя макроса, за которым следует список фактических аргументов в скобках, разделенных запятыми. Вызов макроса не должен ограничиваться одной логической строкой — он может занимать любое количество строк в исходном файле. Количество аргументов должно соответствовать количеству параметров в определении макроса. При развёртывании макроса каждое использование параметра в его теле заменяется токенами соответствующего аргумента. (Вы можете не использовать все параметры в теле макроса.)
В качестве примера, вот макрос, вычисляющий минимум из двух числовых значений, как он определяется во многих программах на C, и некоторые примеры его использования.
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) x = min(a, b); → x = ((a) < (b) ? (a) : (b)); y = min(1, 2); → y = ((1) < (2) ? (1) : (2)); z = min(a + 28, *p); → z = ((a + 28) < (*p) ? (a + 28) : (*p));
(В этом небольшом примере вы уже можете увидеть несколько опасностей использования аргументов макросов. Подробные объяснения см. в разделе Ловушки макросов.)
Пробелы в начале и конце каждого аргумента отбрасываются, а все пробелы между токенами аргумента сокращаются до одного пробела. Скобки внутри каждого аргумента должны быть сбалансированы; запятая внутри таких скобок не заканчивает аргумент. Однако, нет требования по балансировке квадратных скобок или фигурных скобок, и они не препятствуют запятой в разделителях аргументов. Таким образом,
macro (array[x = y, x + 1])
передаёт два аргумента macro: array[x = y и x +
1]. Если вы хотите предоставить array[x = y, x + 1] в качестве аргумента, вы можете написать его как array[(x = y, x + 1)], что эквивалентно коду C.
Все аргументы макроса полностью развёртываются как макросы перед тем, как они подставляются в тело макроса. После подстановки весь текст снова сканируется на макросы для развёртывания, включая аргументы. Это правило может показаться странным, но оно тщательно разработано, чтобы вы не беспокоились о том, является ли какой-либо вызов функции на самом деле вызовом макроса. Тем не менее, вы можете столкнуться с проблемами, если попытаетесь быть слишком хитроумным. Подробное обсуждение см. в разделе Предварительный просмотр аргументов.
Например, min (min (a, b), c) сначала развёртывается до
min (((a) < (b) ? (a) : (b)), (c))
а затем до
((((a) < (b) ? (a) : (b))) < (c) ? (((a) < (b) ? (a) : (b))) : (c))
(Перевод строки показан здесь для ясности, но на самом деле не будет генерироваться.)
Вы можете оставить аргументы макроса пустыми; это не ошибка для препроцессора (но многие макросы затем развёрнутся в недопустимый код). Вы не можете полностью опустить аргументы; если макрос принимает два аргумента, то в списке аргументов должно быть ровно одна запятая на верхнем уровне. Вот несколько забавных примеров, использующих min:
min(, b) → (( ) < (b) ? ( ) : (b)) min(a, ) → ((a ) < ( ) ? (a ) : ( )) min(,) → (( ) < ( ) ? ( ) : ( )) min((,),) → (((,)) < ( ) ? ((,)) : ( )) min() error→ macro "min" requires 2 arguments, but only 1 given min(,,) error→ macro "min" passed 3 arguments, but takes just 2
Пробел не является токеном препроцессора, поэтому, если макрос foo принимает один аргумент, foo () и foo ( ) оба передают ему пустой аргумент. Предыдущие реализации и документация GNU препроцессора были неверны в этом отношении, настаивая на том, что функцию-макрос, принимающую один аргумент, необходимо передавать пробел, если требовался пустой аргумент.
Параметры макроса, встречающиеся внутри строковых литералов, не заменяются соответствующими фактическими аргументами.
#define foo(x) x, "x" foo(bar) → bar, "x"
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-13.3.0/cpp/Macro-Arguments.html