Загрузка [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js
 

printПрепроцессор

printМакросы

Препроцессор – это программа, вызываемая компилятором языков С и C++ перед процессом трансляции, которая выполняет команды, начинающиеся с #, делает замены в тексте программы, заданные макросами, и удаляет комментарии.

Макросы могут быть двух видов:
#define имя   – простая замена
#define "имя"("параметры") quad "текст" – замена с параметрами

Первый вариант подставляет указанный текст вместо имени. Определяемое таким образом имя нельзя использовать как идентификатор, оно становится "резервированным", поэтому рекомендуется писать имена макросов полностью прописными буквами.

#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"

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

#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);
}
Ввод:

Выполнить
Вывод:

Если нужно вставить аргумент в текст, в котором до или после места вставки находятся буквы или цифры, имя другого параметра, то до или после имени параметра нужно написать ##. Таким образом можно создавать идентификаторы, используя аргументы макроса.

#define A(i) a##i
#define SIZE(x) x##_size
#define MAX_SIZE(x) (sizeof(x)/sizeof(x[0]))
int a1,a2,a3,a4,a5;
double arr[100]; // массив
int arr_size=5; // текущий размер массива
...
A(1)=10; // a1=10;
A(2)=A(1);
for(int i=0; i<SIZE(arr); ++i) arr[i]=0;

Параметры не подставляются в текстовых константах. Для создания строки из аргумента в тексте подстановки перед именем параметра нужно указать символ #. Строковая константа разбивается при этом на части, каждая часть записывается в отдельных кавычках, # "параметр" записывается вместо одной из частей. Для получения символьной константы можно взять 0-й символ из строковой: (# "параметр" [0]).

Пример макроса для отладочной печати: печатаются не только значения, но и сами выражения; выбор нужного формата выполняется с помощью операции "выбора по типу".

#include <stdio.h>
#define PRINT(x) printf(_Generic(x, int: #x"=%d\n",\
   double: #x"=%lg\n",\
   char*: #x"=%s\n",\
   default: #x"=?\n"),x);
int main() {
  int a=10;
  double d=2.5;
  char text[]="Hello";
  PRINT(a+5);
  PRINT(2*d);
  PRINT(text);
  PRINT(text[1]);
  PRINT(a+5LL);
}
Ввод:

Выполнить
Вывод:

Если в тексте подстановки встречается уже использованный макрос, то повторно он не применяется (рекурсивная подстановка невозможна).

#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 значений:

#define PRINT_ALL(x, ...) PRINT(x) __VA_OPT__(; PRINT2(__VA_ARGS__))
#define PRINT2(x, ...) PRINT(x) __VA_OPT__(; PRINT3(__VA_ARGS__))
#define PRINT3(x, ...) PRINT(x) __VA_OPT__(; PRINT45(__VA_ARGS__))
#define PRINT45(x, ...) PRINT(x) __VA_OPT__(; PRINT(__VA_ARGS__))
...
PRINT_ALL(a,b,c*5);
PRINT_ALL(arr[1],arr[2]);

Команда
#undef "имя"
позволяет убрать макрос, чтобы он стал доступным в качестве обычного идентификатора или можно было задать другой макрос.

loading