Макросы

Последнее обновление: 22.01.2024

Все идентификаторы, определяемые с помощью директив #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;
}

То есть данный код 4 раза выведет на консоль строку "Hello World! \n".

Подобные определения директивы #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 min(a,b) (a < b ? a : b)

int main(void)
{
	int x = 23;
	int y = 14;
	int z = min(x,y);
	printf("min = %d", z);	// min = 14
	return 0;
}

То есть в данном случае после работы препроцессора вместо строки int z = min(x,y); мы получим строку:

int z = (x < y ? 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;
}

Здесь макрос SQUARE принимает один параметр и возводит его в квадрат. Однако при вызове макроса

int x = SQUARE(4+2);

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

int x = 4+2*4+2;

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

#include <stdio.h>

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

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

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

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

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

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

#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. Или аналогичный пример:

#include <stdio.h>
#define unite(a,b,c) a##b##c;

int main(void)
{
	int x = unite(2, 81, 34);	// 28134
	printf("%d \n", x);
	return 0;
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850