Директивы #if, #elif, #else и #endif (C/C++)

Директива #if с директивами #elif, #else и #endif управляет компиляцией частей исходного файла. Если написанное выражение (после #if) имеет ненулевое значение, группа строк сразу после директивы #if хранится в модуле перевода.

грамматики

условный :
If-part elif-partsoptelse-partoptendif-line

if-part :
Текст строки if

if-line :
#if констант-выражение
идентификатор #ifdef
идентификатор #ifndef

elif-parts :
текст с элифовой линией
текст elif-parts elif-line

elif-line :
#elif констант-выражение

else-part :
Текст в другой строке

else-line :
#else

endif-line :
#endif

Замечания

Каждая директива #if в исходном файле должна соответствовать закрывающей директиве #endif . Любое количество директив #elif может отображаться между директивами #if и #endif , но в большинстве случаев допускается одна директива #else . Директива #else, если она присутствует, должна быть последней директивой перед #endif.

Директивы #if, #elif, #else и #endif могут вложены в текстовые части других директив #if. Каждая вложенная директива #else, #elif или #endif принадлежит ближайшей предыдущей директиве #if.

Все директивы условной компиляции, такие как #if и #ifdef, должны соответствовать закрывающей директиве #endif до окончания файла. В противном случае создается сообщение об ошибке. Если директивы условной компиляции содержатся во включаемых файлах, они должны удовлетворять одинаковым условиям: в конце включаемого файла не должно оставаться непарных директив условной компиляции.

Замена макросов выполняется в части строки, которая следует #elif команде, поэтому вызов макроса можно использовать в констант-выражении.

Препроцессор выбирает один из указанных вхождения текста для дальнейшей обработки. Блок, указанный в тексте , может быть любой последовательностью текста. Он может занимать несколько строк. Обычно текст — это программный текст , который имеет значение для компилятора или препроцессора.

Препроцессор обрабатывает выделенный текст и передает его компилятору. Если текст содержит директивы препроцессора, препроцессор выполняет эти директивы. Компилируются только текстовые блоки, выбранные препроцессором.

Препроцессор выбирает один текстовый элемент, оценивая константное выражение после каждой #if или директивы #elif, пока не находит истинное (ненулевое) константное выражение. Он выбирает весь текст (включая другие директивы препроцессора, начиная с#) до связанного #elif, #else или #endif.

Если все вхождения констант-выражения являются ложными или если директивы #elif не отображаются, препроцессор выбирает блок текста после предложения #else. Если нет предложения #else, а все экземпляры константного выражения в блоке #if имеют значение false, текстовый блок не выбран.

Константное выражение — это целочисленное константное выражение с этими дополнительными ограничениями:

  • Выражения должны иметь целочисленный тип и могут включать только целые константы, константы символов и определенный оператор.

  • Выражение не может использовать sizeof или оператор приведения типа.

  • Целевая среда может не представлять все диапазоны целых чисел.

  • Перевод представляет тип так же, как и тип intlong, и тот же способ, что unsigned longи unsigned int .

  • Транслятор может преобразовывать символьные константы в набор кодовых значений, отличающийся от набора для целевой среды. Чтобы определить свойства целевой среды, используйте приложение, созданное для этой среды, для проверка значений LIMITS. Макросы H.

  • Выражение не должно запрашивать среду и оставаться изолированным от сведений о реализации на целевом компьютере.

Операторы препроцессора

архитектура

Определяемый оператор препроцессора можно использовать в специальных константных выражениях, как показано в следующем синтаксисе:

defined(identifier)
определенныйидентификатор

Это константное выражение считается истинным (ненулевое), если идентификатор определен в данный момент. В противном случае условие не выполняется (false, значение равно 0). Идентификатор, определенный как пустой текст, считается определенным. Определенныйоператор можно использовать в #if и директиве #elif, но нигде.

В следующем примере директивы #if и #endif управляют компиляцией одного из трех вызовов функций:

#if defined(CREDIT)
    credit();
#elif defined(DEBIT)
    debit();
#else
    printerror();
#endif

Вызов функции credit компилируется, если определен идентификатор CREDIT. Если определен идентификатор DEBIT, компилируется вызов функции debit. Если ни один из этих идентификаторов не определен, компилируется вызов функции printerror. Оба CREDIT и credit являются уникальными идентификаторами в C и C++, так как их варианты отличаются.

В следующем примере в операторах условной компиляции используется ранее определенная символьная константа с именем DLEVEL.

#if DLEVEL > 5
    #define SIGNAL  1
    #if STACKUSE == 1
        #define STACK   200
    #else
        #define STACK   100
    #endif
#else
    #define SIGNAL  0
    #if STACKUSE == 1
        #define STACK   100
    #else
        #define STACK   50
    #endif
#endif
#if DLEVEL == 0
    #define STACK 0
#elif DLEVEL == 1
    #define STACK 100
#elif DLEVEL > 5
    display( debugptr );
#else
    #define STACK 200
#endif

В первом блоке #if показаны два набора вложенных #if, #else и директив #endif. Первый набор директив обрабатывается только в том случае, если выполняется условие DLEVEL > 5. В противном случае операторы после обработки #else .

Директивы #elif и #else во втором примере используются для выбора одного из четырех вариантов на основе значенияDLEVEL. Константе STACK присваивается значение 0, 100 или 200 в зависимости от определения константы DLEVEL. Если DLEVEL больше 5, то компилируется оператор

#elif DLEVEL > 5
display(debugptr);

компилируется и STACK не определен.

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

/*  EXAMPLE.H - Example header file  */
#if !defined( EXAMPLE_H )
#define EXAMPLE_H

class Example
{
    //...
};

#endif // !defined( EXAMPLE_H )

Предыдущий код проверяет, определена ли символьная константа EXAMPLE_H. Если да, файл уже включен и не нуждается в повторной обработке. Если нет, константа EXAMPLE_H определяется, чтобы пометить файл EXAMPLE.H как уже обработанный.

__has_include

Visual Studio 2017 версии 15.3 и более поздних версий: определяет, доступен ли заголовок библиотеки для включения:

#ifdef __has_include
#  if __has_include(<filesystem>)
#    include <filesystem>
#    define have_filesystem 1
#  elif __has_include(<experimental/filesystem>)
#    include <experimental/filesystem>
#    define have_filesystem 1
#    define experimental_filesystem
#  else
#    define have_filesystem 0
#  endif
#endif

См. также

Директивы препроцессора