*Препроцессор* -- это программа, вызываемая компилятором языков С и C++ перед процессом трансляции, которая выполняет команды, начинающиеся с ``#``, делает замены в тексте программы, заданные макросами, и удаляет комментарии.
Макросы могут быть двух видов:\
``#define`` `"имя" quad "текст"` -- простая замена\
``#define`` `"имя"("параметры") quad "текст"` -- замена с параметрами
Первый вариант подставляет указанный текст вместо имени. Определяемое таким образом имя нельзя использовать как идентификатор, оно становится "резервированным", поэтому рекомендуется писать имена макросов полностью прописными буквами.
```c
#define SIZE 100
#define size 10
int main() {
int a[SIZE]; // OK
int size; // Error
}
```
Длинный текст подстановки можно разделить на несколько строк, указав символ ``\`` в конце строки:
```
#define ABC aaa\
bbb\
ccc
```
Некоторые макросы уже определены:
* ``__STDC_VERSION__`` -- поддерживаемая версия языка при компиляции как С
- 199409L -- C95
- 199901L -- C99
- 201112L -- C11
- 201710L -- C17
- 202311L -- C23
* ``__cplusplus`` -- поддерживаемая версия языка при компиляции как С++
- 199711L -- стандарт 1998 с изменениями 2003 и 2005
- 201103L -- C++11
- 201402L -- C++14
- 201703L -- C++17
- 202002L -- C++20
- 202302L -- C++23
* ``__FILE__`` -- имя текущего файла в кавычках
* ``__LINE__`` -- номер текущей строки
* ``__DATE__`` -- текущая дата в кавычках в формате "Mmm dd yyyy"
* ``__TIME__`` -- текущее время в кавычках в формате "hh:mm:ss"
Второй вариант подставляет вместо имени, после которого указаны аргументы в круглых скобках, указанный текст, при этом имена параметров в тексте заменяются на соответствующие аргументы. Аргументы не вычисляются, в отличие от аргументов функции, поэтому при подстановке параметры нужно брать в скобки.
```run-c
#include <stdio.h>
#define SQR1(x) x*x
#define SQR2(x) ((x)*(x))
int main() {
int s1=100/SQR1(2+3); // Error: 100/2+3*2+3
int s2=100/SQR2(2+3); // OK: 100/((2+3)*(2+3))
int a=5;
int s3=SQR2(++a); // Error: ((++a)*(++a))
printf("%d %d %d\n",s1,s2,s3);
}
```
Если нужно вставить аргумент в текст, в котором до или после места вставки находятся буквы или цифры, имя другого параметра, то до или после имени параметра нужно написать ``##``. Таким образом можно создавать идентификаторы, используя аргументы макроса.
Параметры не подставляются в текстовых константах. Для создания строки из аргумента
в тексте подстановки перед именем параметра нужно указать символ ``#``. Строковая константа разбивается при этом на части, каждая часть записывается в отдельных кавычках, ``#`` `"параметр"` записывается вместо одной из частей. Для получения символьной константы можно взять 0-й символ из строковой: ``(#`` `"параметр"` ``[0])``.
Пример макроса для отладочной печати: печатаются не только значения, но и сами выражения; выбор нужного формата выполняется с помощью операции "выбора по типу".
Если в тексте подстановки встречается уже использованный макрос, то повторно он не применяется (рекурсивная подстановка невозможна).
```c
#define A A a
#define F(x) x*F(x)
...
F(A) // -> A*F(A) -> A a*F(A a)
```
У макроса может быть переменное количество аргументов, в этом случае после первых фиксированных параметров нужно указать ... как у функций, в тексте подстановки для получения этих аргументов использовать ``__VA_ARGS__``. Вспомогательная конструкция ``__VA_OPT__(`` `s` ``)`` добавляет в подстановку текст `s`, если список аргументов не пустой.
Пример макроса, который выполняет отладочную печать от 1 до 5 значений: