Директива #define имеет ещё одну интересную возможность: макрос может иметь аргументы, и тогда его называют макрос типа функции или макрос (макроопределение) с аргументами.
Пример 1. Можно определить следующий простой макрос
#define ZIKL1 for (int k=0; k<n; k++)
Но тогда макрос ZIKL1 заменится на оператор цикла, в котором его параметром является обязательно переменная k, которая меняется так, как указано в макроопределении, то есть только от 0 до n-1. Как расширить возможности такого макроса?
Будем использовать макроопределение с аргументами, общий вид которого следующий:
#define имя(арг1, арг2, …, аргN) последовательность_символов
Для нашего примера определим следующее макроопределение:
#define ZIKL2 (var, from, to) for (int var=from; var<to; var++)
C его помощью мы имеем возможность записывать различные заголовки цикла, меняя идентификатор для параметра цикла, его начальное и конечное значения при постоянном шаге изменения, равным +1. Если в тексте программы встретится макроопределение с фактическими параметрами k, 0 и n, то есть
|
|
ZIKL2 (k, 0, n),
то, как и в функции, k формально подставляется вместо var, 0 вместо from и n вместо to. В результате будет сформирован следующий заголовок цикла:
for (int k=0; k<n; k++).
Если бы надо было менять и шаг изменения параметра, то его можно было бы включить в качестве четвёртого параметра макроса с аргументами.
Пример 2. Определим следующий макрос с аргументами
#define MYPRINT (var) printf(#var); \
printf (“=%d\n”, var);
Если перед аргументом в макроопределении стоит знак “#”, то имя фактического параметра будет заключено в двойные кавычки. Тогда вместо, например,
MYPRINT (b)
будет компилироваться следующая строка, полученная в результате макроподстановки:
printf(“b”); printf (“=%d\n”, b);
При этом вместо var будет подставлено b.
Пример 3. Оператор препроцессора ## используется для соединения двух частей.
#define concat(u, v) u ## v
int xy =100;
printf(“%d”, concat(x,y));
Последнюю функцию вывода printf препроцессор преобразует в
printf(“%d”, xy);
и при выполнении будет выведено число 100.
Пример 4. Пусть определён макрос с аргументом для проверки, является ли число v чётным:
#define EVEN(v) v%2==0? 1: 0
Если далее в программе будет записано
r=EVEN(9);
то такой оператор будет работать правильно. Он преобразуется следующим образом:
r=9%2==0? 1: 0;
и проверит на чётность число 9.
Но несмотря на то, что макрос с аргументами чем то похож на функцию, имеется существенное отличие. Продолжим рассмотрение этого примера. Пусть k объявлено и определено как целое число. Тогда если запишем
r=EVEN (k+1);
то будет выполнена следующая подстановка
r=k+1%2==0? 1: 0;
Сначала выполнится целочисленное деление, а затем его результат прибавится к переменной k. То есть макроподстановка выполнится не так, как если бы вместо макроса была бы определена функция, то есть неправильно. Нам надо было, чтобы после препроцессорной обработки получилось
|
|
r=(k+1)%2==0? 1: 0;
Для этого при определении макроса параметр v необходимо заключить в круглые скобки:
#define EVEN(v) (v)%2==0? 1: 0
В тексте программы при использовании такого макроса ничего не меняем:
r=EVEN (k+1);
Использование макроопределений с параметрами вместо функций существенно увеличивает скорость работы программы, так как не надо тратить время на вызов функции и возврат из неё. Но за данное увеличение скорости работы следует платить увеличением размера исполнимого кода программы, так как код макроса дублируется столько раз, сколько раз он записан в программе.