Conditional inclusion
Препроцессор поддерживает условную компиляцию частей исходного файла. Это поведение контролируется директивами
#if
,
#else
,
#elif
,
#ifdef
,
#ifndef
,
#elifdef
,
#elifndef
(начиная с C23)
и
#endif
.
Содержание |
Синтаксис
#if
выражение
|
|||||||||
#ifdef
идентификатор
|
|||||||||
#ifndef
идентификатор
|
|||||||||
#elif
выражение
|
|||||||||
#elifdef
идентификатор
|
(начиная с C23) | ||||||||
#elifndef
идентификатор
|
(начиная с C23) | ||||||||
#else
|
|||||||||
#endif
|
|||||||||
Объяснение
Условный блок препроцессора начинается с директивы
#if
,
#ifdef
или
#ifndef
, затем опционально включает любое количество директив
#elif
,
#elifdef
или
#elifndef
(начиная с C23)
, затем опционально включает не более одной директивы
#else
и завершается директивой
#endif
. Любые внутренние условные блоки препроцессора обрабатываются отдельно.
Каждая из директив
#if
,
#ifdef
,
#ifndef
,
#elif
,
#elifdef
,
#elifndef
(начиная с C23)
и
#else
управляет блоком кода до первой директивы
#elif
,
#elifdef
,
#elifndef
(начиная с C23)
,
#else
или
#endif
, не принадлежащей ни одному из внутренних блоков условной препроцессорной обработки.
#if
,
#ifdef
и
#ifndef
директивы проверяют указанное условие (см. ниже) и, если оно оценивается как истинное, компилируют управляемый блок кода. В этом случае последующие
#else
,
#elifdef
,
#elifndef
,
(since C23)
и
#elif
директивы игнорируются. В противном случае, если указанное условие оценивается как ложное, управляемый блок кода пропускается и обрабатывается последующая
#else
,
#elifdef
,
#elifndef
,
(since C23)
или
#elif
директива (если есть). Если последующей директивой является
#else
, блок кода, управляемый директивой
#else
, компилируется безусловно. В противном случае директива
#elif
,
#elifdef
, или
#elifndef
(since C23)
действует как директива
#if
: проверяет условие, компилирует или пропускает управляемый блок кода в зависимости от результата, и в последнем случае обрабатывает последующие
#elif
,
#elifdef
,
#elifndef
,
(since C23)
и
#else
директивы. Условный блок препроцессора завершается директивой
#endif
.
Условное выполнение
#if, #elif
Выражение является константным выражением, использующим только константы и идентификаторы, определённые с помощью директивы #define . Любой идентификатор, не являющийся литералом и не определённый с помощью директивы #define , вычисляется в 0 за исключением true , которое вычисляется в 1 (начиная с C23) .
Выражение может содержать унарные операторы в форме
defined
identifier
или
defined (
identifier
)
, которые возвращают
1
, если
identifier
был определён с помощью директивы
#define
, и
0
в противном случае.
В этом контексте
__has_include
,
__has_embed
и
__has_c_attribute
обрабатываются как если бы они были именами определённых макросов.
(начиная с C23)
Если
expression
вычисляется в ненулевое значение, управляемый блок кода включается, в противном случае пропускается. Если любой используемый идентификатор не является константой, он заменяется на
0
.
|
В контексте директивы препроцессора, выражение
|
(since C23) |
Примечание: До
DR 412
,
#if
cond1
...
#elif
cond2
отличается от
#if
cond1
...
#else
с последующим
#if
cond3
, потому что если
cond1
истинно, второй
#if
пропускается и
cond3
не обязательно должно быть корректно сформированным, тогда как
#elif
требует, чтобы
cond2
было валидным выражением. Начиная с
DR 412
,
#elif
, который ведет к пропускаемому блоку кода, также пропускается.
Комбинированные директивы
Проверяет, был ли идентификатор определён как имя макроса .
#ifdef
identifier
по сути эквивалентно
#if defined
identifier
.
#ifndef
identifier
по сути эквивалентно
#if !defined
identifier
.
|
|
(начиная с C23) |
Примечания
В то время как
#elifdef
и
#elifndef
директивы предназначены для C23, реализации могут переносить их в более старые языковые режимы как соответствующие расширения.
Пример
#define ABCD 2 #include <stdio.h> int main(void) { #ifdef ABCD printf("1: yes\n"); #else printf("1: no\n"); #endif #ifndef ABCD printf("2: no1\n"); #elif ABCD == 2 printf("2: yes\n"); #else printf("2: no2\n"); #endif #if !defined(DCBA) && (ABCD < 2 * 4 - 3) printf("3: yes\n"); #endif // C23 directives #elifdef/#elifndef #ifdef CPU printf("4: no1\n"); #elifdef GPU printf("4: no2\n"); #elifndef RAM printf("4: yes\n"); // selected in C23 mode, may be selected in pre-C23 mode #else printf("4: no3\n"); // may be selected in pre-C23 mode #endif }
Возможный вывод:
1: yes 2: yes 3: yes 4: yes
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| DR 412 | C89 |
требовалось, чтобы выражение в неудавшемся
#elif
было корректным
|
неудавшийся
#elif
пропускается
|
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
-
- 6.10.1 Условное включение (стр: TBD)
- Стандарт C17 (ISO/IEC 9899:2018):
-
- 6.10.1 Условное включение (стр: 118-119)
- Стандарт C11 (ISO/IEC 9899:2011):
-
- 6.10.1 Условное включение (стр: 162-164)
- Стандарт C99 (ISO/IEC 9899:1999):
-
- 6.10.1 Условное включение (стр. 147-149)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
-
- 3.8.1 Условное включение
Смотрите также
|
C++ documentation
для
Условное включение
|