Директивы #if, #elif, #else и #endif (C/C++)
Директива #if с директивами #elif, #else и #endif управляет компиляцией частей исходного файла. Если написанное выражение (после #if) имеет ненулевое значение, группа строк сразу после директивы #if хранится в модуле перевода.
грамматики
условный :
If-part elif-partsopt else-partopt endif-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
или оператор приведения типа.Целевая среда может не представлять все диапазоны целых чисел.
Перевод представляет тип так же, как и тип
int
long
, и тот же способ, чтоunsigned long
иunsigned int
.Транслятор может преобразовывать символьные константы в набор кодовых значений, отличающийся от набора для целевой среды. Чтобы определить свойства целевой среды, используйте приложение, созданное для этой среды, для проверки значений пределов . Макросы H .
Выражение не должно запрашивать среду и оставаться изолированным от сведений о реализации на целевом компьютере.
Операторы препроцессора
архитектура
Определяемый оператор препроцессора можно использовать в специальных константных выражениях, как показано в следующем синтаксисе:
defined( идентификатор )
определенный идентификатор
Это константное выражение считается истинным (ненулевое), если идентификатор определен в данный момент. В противном случае условие не выполняется (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