Замена текстовых макросов
Препроцессор поддерживает замену текстовых макросов и замену текстовых макросов, подобных функциям.
Синтаксис
#define идентификатор список-замены (необязательно) | (1) | |
#define идентификатор ( параметры ) список-замены | (2) | |
#define идентификатор ( параметры, ... ) список-замены | (3) | (начиная с C99) |
#define идентификатор ( ... ) список-замены | (4) | (начиная с C99) |
#undef идентификатор | (5) |
Пояснение
Директивы #define
Директивы #define определяют идентификатор как макрос, то есть указывают компилятору заменить все последующие вхождения идентификатора на список-замены, который может быть дополнительно обработан. Если идентификатор уже определён как макрос любого типа, программа некорректна, если только определения не идентичны.
Объектоподобные макросы
Объектоподобные макросы заменяют каждое вхождение определённого идентификатора на список-замены. Версия (1) директивы #define работает именно так.
Функционоподобные макросы
Функционоподобные макросы заменяют каждое вхождение определённого идентификатора на список-замены, дополнительно принимая некоторое количество аргументов, которые затем заменяют соответствующие вхождения любого из параметров в списке-замене.
Синтаксис вызова функционоподобного макроса похож на синтаксис вызова функции: каждое вхождение имени макроса, за которым следующим токеном препроцессора идёт (, начинает последовательность токенов, заменяемую списком-замены. Эта последовательность заканчивается соответствующим токеном ), пропуская промежуточные парные круглые скобки.
Количество аргументов должно совпадать с количеством аргументов в определении макроса (параметрами), иначе программа некорректна. Если идентификатор не используется в функциональной нотации, то есть за ним не следуют круглые скобки, он вообще не заменяется.
Версия (2) директивы #define определяет простой функционоподобный макрос.
Версия (3) директивы #define определяет функционоподобный макрос с переменным числом аргументов. К дополнительным аргументам можно обратиться с помощью идентификатора __VA_ARGS__, который затем заменяется аргументами, переданными вместе с заменяемым идентификатором.
Версия (4) директивы #define определяет функционоподобный макрос с переменным числом аргументов, но без обычных аргументов. К аргументам можно обратиться только с помощью идентификатора __VA_ARGS__, который затем заменяется аргументами, переданными вместе с заменяемым идентификатором.
| В версиях (3, 4) список-замены может содержать последовательность токенов #define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)
G(a, b, c) // replaced by f(0, a, b, c)
G(a, ) // replaced by f(0, a)
G(a) // replaced by f(0, a)
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
SDEF(foo); // replaced by S foo;
SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 };
| (начиная с C23) |
Примечание: если аргумент функционоподобного макроса содержит запятые, не защищённые парными круглыми скобками (например, macro(array[x = y, x + 1]) или atomic_store (p, (struct S){ a, b });), запятая интерпретируется как разделитель аргументов макроса, что приводит к ошибке компиляции из-за несовпадения количества аргументов.
Операторы # и ##
В функционоподобных макросах оператор # перед идентификатором в списке-замене выполняет подстановку параметра для идентификатора и заключает результат в кавычки, фактически создавая строковый литерал. Кроме того, препроцессор добавляет обратные косые черты для экранирования кавычек, обрамляющих встроенные строковые литералы, если они есть, и при необходимости удваивает обратные косые черты внутри строки. Все начальные и конечные пробельные символы удаляются, а любая последовательность пробельных символов в середине текста (но не внутри встроенных строковых литералов) сводится к одному пробелу. Эта операция называется «преобразованием в строку». Если результат такого преобразования не является допустимым строковым литералом, поведение не определено.
| Когда #define showlist(...) puts(#__VA_ARGS__)
showlist(); // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int")
| (начиная с C99) |
Оператор ## между любыми двумя соседними идентификаторами в списке-замене выполняет подстановку параметров для этих двух идентификаторов, а затем объединяет результат. Эта операция называется «конкатенацией» или «склеиванием токенов». Склеивать можно только токены, которые вместе образуют допустимый токен: идентификаторы, образующие более длинный идентификатор, цифры, образующие число, или операторы + и =, образующие +=. Комментарий нельзя создать склеиванием / и *, поскольку комментарии удаляются из текста до рассмотрения подстановки макросов. Если результат конкатенации не является допустимым токеном, поведение не определено.
Примечание: некоторые компиляторы предоставляют расширение, позволяющее ## стоять после запятой и перед __VA_ARGS__. В этом случае ## ничего не делает, если __VA_ARGS__ не пуст, но удаляет запятую, если __VA_ARGS__ пуст: это позволяет определять такие макросы, как fprintf (stderr, format, ##__VA_ARGS__).
Порядок вычисления операторов # и ## не определён.
Директива #undef
Директива #undef отменяет определение идентификатора, то есть отменяет предыдущее определение идентификатора директивой #define. Если с идентификатором не связан макрос, директива игнорируется.
Предопределённые макросы
Следующие имена макросов предопределены в любой единице трансляции:
|
__STDC__ | раскрывается в целочисленную константу 1. Этот макрос предназначен для обозначения соответствующей требованиям реализации (константа-макрос) |
|
__STDC_VERSION__
(C95) | раскрывается в целочисленную константу типа long, значение которой увеличивается с каждой версией стандарта C:
|
|
__STDC_HOSTED__
(C99) | раскрывается в целочисленную константу 1, если реализация является хостовой (работает под управлением ОС), и в 0, если она автономная (работает без ОС) (константа-макрос) |
|
__FILE__ | раскрывается в имя текущего файла в виде строкового литерала; может быть изменено директивой #line (константа-макрос) |
|
__LINE__ | раскрывается в номер строки исходного файла в виде целочисленной константы; может быть изменён директивой #line (константа-макрос) |
|
__DATE__ | раскрывается в дату трансляции в виде строкового литерала формата "Mmm dd yyyy". Название месяца формируется так же, как asctime, а первый символ "dd" является пробелом, если число месяца меньше 10 (константа-макрос) |
|
__TIME__ | раскрывается во время трансляции в виде строкового литерала формата "hh:mm:ss", как и время, сгенерированное asctime() (константа-макрос) |
|
__STDC_UTF_16__
(C23) | раскрывается в 1, указывая, что char16_t использует кодировку UTF-16 (константа-макрос) |
|
__STDC_UTF_32__
(C23) | раскрывается в 1, указывая, что char32_t использует кодировку UTF-32 (константа-макрос) |
|
__STDC_EMBED_NOT_FOUND____STDC_EMBED_FOUND____STDC_EMBED_EMPTY__
(C23) | раскрываются соответственно в 0, 1 и 2 (константа-макрос) |
Следующие дополнительные имена макросов могут быть предопределены реализацией:
|
__STDC_ISO_10646__
(C99) | раскрывается в целочисленную константу вида yyyymmL, если wchar_t использует Unicode; дата указывает последнюю поддерживаемую редакцию Unicode (константа-макрос) |
|
__STDC_IEC_559__
(C99) | раскрывается в 1, если поддерживается IEC 60559 (устарело)(начиная с C23) (константа-макрос) |
|
__STDC_IEC_559_COMPLEX__
(C99) | раскрывается в 1, если поддерживается комплексная арифметика IEC 60559 (устарело)(начиная с C23) (константа-макрос) |
|
__STDC_UTF_16__
(C11) | раскрывается в 1, если char16_t использует кодировку UTF-16 (константа-макрос) |
|
__STDC_UTF_32__
(C11) | раскрывается в 1, если char32_t использует кодировку UTF-32 (константа-макрос) |
|
__STDC_MB_MIGHT_NEQ_WC__
(C99) | раскрывается в 1, если 'x' == L'x' может быть ложным для символа базового набора символов, например в системах на основе EBCDIC, использующих Unicode для wchar_t (константа-макрос) |
|
__STDC_ANALYZABLE__
(C11) | раскрывается в 1, если поддерживается анализируемость (константа-макрос) |
|
__STDC_LIB_EXT1__
(C11) | раскрывается в целочисленную константу 201112L, если поддерживаются интерфейсы проверки границ (константа-макрос) |
|
__STDC_NO_ATOMICS__
(C11) | раскрывается в 1, если не поддерживаются атомарные типы и библиотека атомарных операций (константа-макрос) |
|
__STDC_NO_COMPLEX__
(C11) | раскрывается в 1, если не поддерживаются комплексные типы и библиотека комплексной математики (константа-макрос) |
|
__STDC_NO_THREADS__
(C11) | раскрывается в 1, если не поддерживается многопоточность (константа-макрос) |
|
__STDC_NO_VLA__
(C11) | раскрывается в 1, если не поддерживаются массивы переменной длины и типы с переменным модификатором(до C23)с автоматической длительностью хранения(начиная с C23) (константа-макрос) |
|
__STDC_IEC_60559_BFP__
(C23) | раскрывается в 202311L, если поддерживается двоичная арифметика с плавающей запятой IEC 60559 (константа-макрос) |
|
__STDC_IEC_60559_DFP__
(C23) | раскрывается в 202311L, если поддерживается десятичная арифметика с плавающей запятой IEC 60559 (константа-макрос) |
|
__STDC_IEC_60559_COMPLEX__
(C23) | раскрывается в 202311L, если поддерживается комплексная арифметика IEC 60559 (константа-макрос) |
|
__STDC_IEC_60559_TYPES__
(C23) | раскрывается в 202311L, если поддерживаются типы обмена и расширенные типы IEC 60559 (константа-макрос) |
Значения этих макросов (за исключением __FILE__ и __LINE__) остаются неизменными на протяжении всей единицы трансляции. Попытки переопределить эти макросы или отменить их определения приводят к неопределённому поведению.
| Предопределённая переменная | (начиная с C99) |
Пример
#include <stdio.h>
// make function factory and use it
#define FUNCTION(name, a) int fun_##name(int x) { return (a) * x; }
FUNCTION(quadruple, 4)
FUNCTION(double, 2)
#undef FUNCTION
#define FUNCTION 34
#define OUTPUT(a) puts( #a )
int main(void)
{
printf("quadruple(13): %d\n", fun_quadruple(13) );
printf("double(21): %d\n", fun_double(21) );
printf("%d\n", FUNCTION);
OUTPUT(billion); // note the lack of quotes
}
Вывод:
quadruple(13): 52 double(21): 42 34 billion
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C.
| DR | Применён к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| DR 321 | C99 | было неясно, всегда ли L'x' == 'x' выполняетсядля базового набора символов |
Для этой цели добавлен __STDC_MB_MIGHT_NEQ_WC__ |
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
- 6.10.4 Замена макросов (с. 187–184)
- 6.10.9 Имена предопределённых макросов (с. 186–188)
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.10.3 Замена макросов (с. 121–126)
- 6.10.8 Имена предопределённых макросов (с. 127–129)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.10.3 Замена макросов (с. 166–173)
- 6.10.8 Имена предопределённых макросов (с. 175–176)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.10.3 Замена макросов (с. 151–158)
- 6.10.8 Имена предопределённых макросов (с. 160–161)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
- 3.8.3 Замена макросов
- 3.8.8 Имена предопределённых макросов
См. также
| Документация по C++ о замене текстовых макросов |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/preprocessor/replace