Перейти к основному содержимому

Макросы

Все идентификаторы, определяемые с помощью директив #define, которые предполагают замену на определенную последовательность символов, еще называют макросами.

Макросы позволяют определять замену не только для отдельных символов, но и для целых выражений:

#include <stdio.h>

#define HELLO printf("Hello World! \n")
#define FOR for(int i=0; i<4; i++)

int main(void)
{
FOR HELLO;
return 0;
}

Макрос HELLO определяет вывод на консоль строки "Hello World! \n". А макрос FOR определяет цикл, который отрабатывает 4 раза. В итоге после обработки препроцессора функция main будет выглядеть следующим образом:

int main(void)
{
for(int i=0; i<4; i++) printf("Hello World! \n");
return 0;
}

Подобные определения директивы #define имеют один недостаток, последовательность символов, которая используется директивой фиксирована. Например, здесь везде, где встретится в исходном коде идентификатор HELLO, выводится строка "Hello World!". Если нужно передавать строку динамически, то можно задать макроопределение с параметрами в следующей форме:


#define имя_макроса(список_параметров) последовательность_символов

Список_параметров - это список идентификаторов, разделенных запятыми.

Внимание !

Между именем макроса и открывающей скобкой не должно быть пробелов.

Для обращения к макросу применяется конструкция:

имя_макроса(список_аргументов)

Список_аргументов - это набор значений, которые передаются для каждого параметра макроса.

Например:

#define print(a) printf("%d \n", a)

Здесь print - это имя макроса или идентификатор, после которого в скобках указан параметр a. Этот параметр будет представлять любое целое число. И любой вызов макроса print будет заменяться на строку printf("%d \n", a).

main.c
#include <stdio.h>
#define print(a) printf("%d \n", a)

int main(void)
{
int x = 10;
print(x);
int y =20;
print(y);
print(22);
return 0;
}

Или более сложный пример: определение макроса swap(t,x,y), который обменивает местами значения двух аргументов типа t:

main.c
#include <stdio.h>

#define t int
#define swap(t, x, y) { t temp = x; x = y; y=temp;}

int main(void)
{
t x = 4;
t y = 10;
swap(t, x, y)
printf("x=%d \t y=%d", x, y);
return 0;
}

Макрос swap применяет блок для обмена значениями. Причем данный макрос фактически универсален: неважно, какой тип у переменных x и y.

Внимание !

При определении макроса его параметры следует заключать в скобки. Например:

main.c
#include <stdio.h>

#define SQUARE(n) n*n

int main(void)
{
int x = SQUARE(4+2);

printf("x = %d\n", x); // x = 14
return 0;
}

Некорректный результат, а именно число 14, а не 36, получился потому, что данный вызов макроса будет разворачиваться в выражение

int x = 4+2*4+2;

Чтобы получить нужный результат, необходимо поместить параметры макроса в скобки:

#define SQUARE(n) (n)*(n)   // параметры в скобках

Препроцессорные операции

При обработки исходного кода препроцессор может выполнять две операции: # и ##.

Операция # позволяет заключать текст параметра, который следует после операции, в кавычки:

main.c
#include <stdio.h>
#define print_int(n) printf(#n"=%d \n",n);

int main(void)
{
int x = 23;
print_int(x); // x=23
int y = 14;
print_int(y); // y=14
int number = 203;
print_int(number); // number=203
return 0;
}

Директива ## позволяет объединять две лексемы:

main.c
#include <stdio.h>
#define print(a,b,c) printf("%d", a##b##c);

int main(void)
{
print(2, 81, 34); // 28134
return 0;
}

Здесь склеиваются три числа, которые передаются в макрос print.

Макросы против функций

Задачи можно решать и с помощью макроса и с помощью функций. Нет строгих правил что лучше. Можно дать лишь несколько рекомендаций, которые позволят принять правильное решение.

Если макрос вызывать 20 раз, то в программу вставляются 20 строк этого макроса. Если функцию вызвать 20 раз, то в программе содержится единственный экземпляр ее операторов, но программа совершает переход к фрагменту кода функции и обратно, что в целом замедляет ее работу, т.е. Этот процесс может занимать больше времени чем выполнение встраиваемого кода. Для макроса не важны типы и размеры, он манипулирует со строками.

Стоит выбрать макрос вместо функции если:

  1. Если макрос можно описать одной строкой

  2. Можно использовать макрос для реализации простых функций

  3. Если вы намерены использовать макрос вместо функции для ускорения, стоит подумать, а даст ли это желаемый эффект. Воспользуйтесь профилированием, для определения скорости работы программы

Самостоятельная работа

Задачи

Задача 1

Что будет напечатано ?

main.c
#define A B + 1
#define B 2

int a;
a = A + 2;
printf("a = %d\n", a);

Задача 2

Что будет напечатано ?

main.c
#define HALF(x) x/2
#define B 2

int a = 5, b;
b = HALF(a + 5);
printf("a = %d b = %d\n",a, b);