Макросы
Все идентификаторы, определяемые с помощью директив #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)
.
#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:
#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.
При определении макроса его параметры следует заключать в скобки. Например:
#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) // параметры в скобках
Препроцессорные операции
При обработки исходного кода препроцессор может выполнять две операции: # и ##.
Операция # позволяет заключать текст параметра, который следует после операции, в кавычки:
#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;
}
Директива ## позволяет объединять две лексемы:
#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
Что будет напечатано ?
#define A B + 1
#define B 2
int a;
a = A + 2;
printf("a = %d\n", a);
Задача 2
Что будет напечатано ?
#define HALF(x) x/2
#define B 2
int a = 5, b;
b = HALF(a + 5);
printf("a = %d b = %d\n",a, b);