double arrow

Макрос с аргументами

Директива #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);

Использование макроопределений с параметрами вместо функций существенно увеличивает скорость работы программы, так как не надо тратить время на вызов функции и возврат из неё. Но за данное увеличение скорости работы следует платить увеличением размера исполнимого кода программы, так как код макроса дублируется столько раз, сколько раз он записан в программе.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



Сейчас читают про: