Namespaces
Variants

Conditional inclusion

From cppreference.net

Препроцессор поддерживает условную компиляцию частей исходного файла. Это поведение контролируется директивами #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 .

В контексте директивы препроцессора, выражение __has_c_attribute определяет, поддерживается ли указанный токен атрибута и его поддерживаемая версия. См. Тестирование атрибутов .

(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 .

#elifdef identifier по сути эквивалентно #elif defined identifier .

#elifndef identifier по сути эквивалентно #elif !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 для Условное включение