Ifndef c что это

от admin

Препроцессорные директивы

Директива #include вставляет код из указанного файла в текущий файл, то есть, просто подключив другой файл, мы можем пользоваться его функциями, классами, переменными. Заголовочные файлы обычно находятся либо в текущей директории, либо в стандартном системном каталоге.

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

Если подключаемый файл не найден, процесс компиляции завершается с ошибкой.

Директива #define

Директива #define принимает две формы:

  • определение констант;
  • определение макросов.
Определение констант

При использовании имени константы — nameToken , оно будет заменено значением value , то есть, грубо говоря — это та же самая переменная, значение которой изменить нельзя. Смотрим пример использования константы:

Как видите, для доступа к значению константы, просто используем её имя.

Определение параметризованных макросов

К примеру определим макрос, который будет возвращать максимальное из двух значений.

Внимание, для определения многострочного макроса, в каждой строке, в конце, должен ставиться символ , что сообщает препроцессору, что макрос еще не завершен.

Директива #undef

Директива #undef переопределяет константу или препроцессорный макрос, ранее определенный с помощью директивы #define .

Давайте посмотрим пример использования директивы #undef :

Как правило, директива #undef используются для снятия, ранее определенной константы или макроса, в небольшой области программы. Это делается для того, чтобы для всей программы, макроc или константа оставались, а для некоторой области, эти же макрос или константа могут быть переопределены. Небезопасно было бы во всей программе переопределять константу, но в короткий области, это сравнительно безопасно. Директива #undef является единственным способом создания этой области, так как область действия макросов или констант действует от директивы #define до #undef .

Директива #if

Директива #if проверяет, является ли значение value истиной и, если это так, то выполняется код, который стоит до закрывающей директивы #endif . В противном случае, код внутри #if не будет компилироваться, он будет удален компилятором, но это не влияет на исходный код в исходнике.

Обратите внимание, что в #if могут быть вложенные директивы #elsif и #else . Ниже показан пример кода для комментирования блоков кода, используя следующую конструкцию:

Если у вас в программе есть блоки кода, которые содержат многострочные комментарии и вам требуется обернуть полностью этот блок кода в комментарий — ничего не получится, если вы воспользуетесь /*многострочный комментарий*/ . Другое дело — конструкция директив #if #endif .

Директива #ifdef

Директива #ifdef проверяет, был ли ранее определен макрос или символическая константа как #define . Если — да, компилятор включает в программу код, который находится между директивами #ifdef и #else , если nameToken ранее определен не был, то выполняется код между #else и #endif , или, если нет директивы #else , компилятор сразу переходит к #endif . Например, макрос __cpp определен в C++, но не в Си. Вы можете использовать этот факт для смешивания C и C++ кода, используя директиву #ifdef :

Директива #ifndef

Директива #ifndef проверяет, был ли ранее определен макрос или символическая константа как #define . Если — да, компилятор включает в программу код, который находится между директивами #else и #endif , если nameToken ранее определен не был, то выполняется код между #ifndef и #else , или, если нет директивы #else , компилятор сразу переходит к #endif . Директива #ifndef может быть использована для подключения заголовочных файлов. если они не подключены, для этого использовать символическую константу, как индикатор подключенного к проекту функционала.

Например, в заголовочном файле есть интерфейс класса, который необходимо подключить к проекту, если ранее этот класс не был подключен.

В этом случае используется пустая символьная константа PRODUCT_H , которая может быть определена в программе только вместе с классом Product . Поэтому, если мы обнаружим, что константа PRODUCT_H уже определена, значит класс тоже и тогда мы исключим повторное определение класса, которое может привести к ошибке переопределения.

Директива #error

Директива #error позволяет отображать в списке ошибок компиляции сообщение, в случае возникновения соответствующей ошибки. Эту директиву наиболее полезно использовать в сочетании с директивами #if , #elsif , #else для проверки компиляции, если некоторое условие не верно. Например:

Препроцессорный макрос __FILE__

Препроцессорный макрос __FILE__ расширяется до полного пути к текущему файлу (исходнику). __FILE__ полезен при создании лог-файла, генерации сообщений об ошибках, предназначенных для программистов, при отладки кода.

Макрос __FILE__ часто используется совместно с макросом __LINE__ , который предоставляет номер текущей строки.

Препроцессорный макрос __LINE__

Макрос __LINE__ разворачивается в текущий номер строки в исходном файле, как целое значение. __LINE__ полезен при создании лог-файла или генерации сообщений об ошибках с указанием номера строки, предназначенных для программистов, при отладки кода.

Макрос __LINE__ часто используется совместно с макросом __FILE__ , который показывает адрес текущего исходного файла.

Препроцессорный макрос __DATE__

Макрос __DATE__ раскрывается в текущую дату (время компиляции) в виде [ммм дд гггг] (например, «Dec 7 2012″), как строка. __DATE__ может быть использован для предоставления информации о времени компиляции.

Вы можете также использовать макрос __TIME__ , чтобы получить текущее время компиляции.

Препроцессорный макрос __TIME__

Макрос __TIME__ раскрывается в текущее время (время компиляции) в формате чч: мм:cc в 24-часовом формате (например, «22:29:12″). Макрос __TIME__ может быть использован для предоставления информации о времени в конкретный момент компиляции.

Препроцессорный макрос __TIMESTAMP__

Макрос __TIMESTAMP__ раскрывается в текущее время (время компиляции) в формате Ddd Mmm Date hh::mm::ss yyyy, время в 24-часовом формате:

  • Ddd это сокращенно день недели,
  • ммм это сокращенно месяц ,
  • Date — текущий день месяца (1-31),
  • гггг — это четыре цифры года.

Например, «Fri Dec 7 00:42:53 2012» . Макрос __TIMESTAMP__ может быть использован для получения информации о дате и времени компиляции.

Вы можете также использовать макрос __TIME__ , чтобы получить текущее время компиляции и макрос __DATE__ для получения даты.

Директива #pragma

Директива #pragma используется для доступа к специфическим расширениям компилятора. Совместное использование директивы #pragma c лексемой once просит компилятор включить файл заголовка только один раз, независимо от того, сколько раз она был импортирован:

В этом примере, директива #pragma once не позволяет включать файл в проект несколько раз, то есть предотвращает переопределение.

Директива #pragma также может быть использована для других целей, например #pragma обычно используется для отключения предупреждений. Например, в MVS:

Директива #pragma в этом примере используется для отключения предупреждений 4018. Для получения дополнительной использования директивы #pragma , обратитесь к документации вашего компилятора.

Макро оператор #

Оператор # текстовую лексему в строку, заключенную в кавычку. Смотрим пример:

Выполняется конкатенация строк и макрос message разворачивается в cout << «Сообщение: GunGamen»; . Обратите внимание на то, что операция # должна использоваться совместно с аргументами, так как # ссылается на аргумент.

Макро оператор ##

Оператор ## принимает две отдельных лексемы и склеивает их вместе, чтобы сформировать один макрос. В результате может получиться имя переменной, имя класса или любой другой идентификатор. Например:

Рассмотрим еще один пример использования оператора ## , в котором объединим две лексемы:

Когда в программе выполняется вызов этого макроса, две лексемы объединяются в одну. Операция ## обязательно должна иметь два операнда.

Зачем пишут #ifndef file_h в начале файла

Для каких ситуаций может быть полезно определение именно таким образом?

Эти директивы предназначены для защиты от многократного включения при наличии сложной иерархии включаемых файлов — если такой файл уже был включен где-то раньше, то и __IMAGE_H__ будет объявлена, а значит, условный #define будет пропущен, вместе со всеми объявлениями до #endif

В некоторых компиляторах есть аналогичные по действию #pragma , например,

Читать:
Как скрыть версию nginx

Но версия с условием наиболее универсальна.

Это же классический include guard, который защищает от повторного включения заголовочных файлов, чтобы не было переопределения одних и тех же функций/классов.

Так обычно делается в заголовочных файлах, включаемых по include . В случае если заголовочный файл будет включен в программу дважды, это вызовет кучу ошибок типа «повторное объявление». Чтобы этого не происходило, этим define объявляют флаг, что файл уже включен и следовательно, когда он будет включен повторно, ifndef не сработает и все содержимое файла будет проигнорировано.

Conditional inclusion

Препроцессор поддерживает условную компиляцию частей исходного файла. Это поведение контролируется #if , #else , #elif , #ifdef , #ifndef , #elifdef , #elifndef (начиная с C++23) и #endif .

Syntax

#if expression
#ifdef identifier
#ifndef identifier
#elif expression
#elifdef identifier (since C++23)
#elifndef identifier (since C++23)
#else
#endif

Explanation

Блок условной предварительной обработки начинается с #if , #ifdef или #ifndef , затем дополнительно включает любое количество #elif , #elifdef или #elifndef (начиная с C++23), затем дополнительно включает не более одной директивы #else и завершается директивой #endif .Любые внутренние условные блоки предварительной обработки обрабатываются отдельно.

Каждая из #if , #ifdef , #ifndef , #elif , #elifdef , #elifndef (начиная с C++23) и #else управляет блоком кода до первого #elif , #elifdef , #elifndef (начиная с C+ +23) , директива #else , #endif не принадлежит никаким внутренним блокам условной предварительной обработки.

#if , #ifdef и #ifndef проверяют указанное условие (см. ниже) и, если оно оценивается как истинное, компилируют контролируемый блок кода. В этом случае последующие директивы #else , #elifdef , #elifndef (начиная с C++23) и #elif игнорируются. В противном случае, если указанное условие оценивается как ложное, контролируемый блок кода пропускается и обрабатывается последующая директива #else , #elifdef , #elifndef (начиная с C++23) или директива #elif (если есть). Если последующей директивой является #else , блок кода, управляемый директивой #else директива безоговорочно скомпилирована. В противном случае #elif , #elifdef или #elifndef (начиная с C++23) действует так же, как если бы она была директивой #if : проверяет условие, компилирует или пропускает контролируемый блок кода на основе результата, и в последнем случае обрабатывает последующие #elif , #elifdef , #elifndef (начиная с C++23) и #else . Блок условной предварительной обработки завершается директивой #endif .

Condition evaluation

#if, #elif

Выражение может содержать:

  • унарные операторы в виде defined идентификатора или defined ( идентификатора ) . Результат равен 1 , если идентификатор был определен как имя макроса , в противном случае результат равен ​0​ ​.
  • (начиная с C++17) выражения __has_include , которые определяют, существует ли заголовок или исходный файл.
  • (начиная с C++20) выражения __has_cpp_attribute , которые определяют, поддерживается ли данный токен атрибута, и его поддерживаемая версия.

После раскрытия всех макросов и оценки defined выражений , __has_include (начиная с C++17) и __has_cpp_attribute (начиная с C++20) любой идентификатор, не являющийся логическим литералом , заменяется числом ​0​ ​ ( включая идентификаторы, которые лексически являются ключевыми словами, а не альтернативными токенами, такими как and ).

Если выражение вычисляется до ненулевого значения,то управляемый блок кода включается и пропускается в противном случае.

Примечание. До решения проблемы CWG 1955 #if cond1 . #elif #elif cond2 отличается от #if cond1 . #else , за которым следует #if cond2 , потому что, если cond1 истинно, второе #if пропускается, а cond2 верно . не обязательно иметь правильный формат, а cond2 #elif #elif быть допустимым выражением. Начиная с CWG 1955, #elif , предшествующий пропущенному блоку кода, также пропускается.

Combined directives

Проверяет, был ли идентификатор определен как имя макроса .

#ifdef идентификатор по существу эквивалентен #if defined идентификатору.

#ifndef по существу эквивалентен идентификатору #if !defined .

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

#elifndef по сути эквивалентен идентификатору #elif !defined .

Notes

Хотя директивы #elifdef и #elifndef нацелены на C++23, в реализациях рекомендуется использовать их для более старых языковых режимов в качестве соответствующих расширений.

Example

Defect reports

Следующие отчеты о дефектах,изменяющих поведение,были применены ретроактивно к ранее опубликованным стандартам C++.

Conditional inclusion

The preprocessor supports conditional compilation of parts of source file. This behavior is controlled by #if , #else , #elif , #ifdef , #ifndef , #elifdef , #elifndef (since C++23) , and #endif directives.

Contents

[edit] Syntax

#if expression
#ifdef identifier
#ifndef identifier
#elif expression
#elifdef identifier (since C++23)
#elifndef identifier (since C++23)
#else
#endif

[edit] Explanation

The conditional preprocessing block starts with #if , #ifdef or #ifndef directive, then optionally includes any number of #elif , #elifdef , or #elifndef (since C++23) directives, then optionally includes at most one #else directive and is terminated with #endif directive. Any inner conditional preprocessing blocks are processed separately.

Each of #if , #ifdef , #ifndef , #elif , #elifdef , #elifndef (since C++23) , and #else directives control the code block until the first #elif , #elifdef , #elifndef (since C++23) , #else , #endif directive not belonging to any inner conditional preprocessing blocks.

#if , #ifdef and #ifndef directives test the specified condition (see below) and if it evaluates to true, compiles the controlled code block. In that case subsequent #else , #elifdef , #elifndef , (since C++23) and #elif directives are ignored. Otherwise, if the specified condition evaluates false, the controlled code block is skipped and the subsequent #else , #elifdef , #elifndef , (since C++23) or #elif directive (if any) is processed. If the subsequent directive is #else , the code block controlled by the #else directive is unconditionally compiled. Otherwise, the #elif , #elifdef , or #elifndef (since C++23) directive acts as if it was #if directive: checks for condition, compiles or skips the controlled code block based on the result, and in the latter case processes subsequent #elif , #elifdef , #elifndef , (since C++23) and #else directives. The conditional preprocessing block is terminated by #endif directive.

[edit] Condition evaluation

[edit] #if, #elif

The expression may contain:

  • unary operators in form defined   identifier or defined ( identifier ) . The result is 1 if the identifier was defined as a macro name, otherwise the result is ​ 0 ​ .
  • (since C++17) __has_include expressions, which detects whether a header or source file exists.
  • (since C++20) __has_cpp_attribute expressions, which detects whether a given attribute token is supported and its supported version.

After all macro expansion and evaluation of defined , __has_include (since C++17) , and __has_cpp_attribute (since C++20) expressions, any identifier which is not a boolean literal is replaced with the number ​ 0 ​ (this includes identifiers that are lexically keywords, but not alternative tokens like and ).

Then the expression is evaluated as an integral constant expression.

If the expression evaluates to nonzero value, the controlled code block is included and skipped otherwise.

Note: Until the resolution of CWG issue 1955, #if cond1 . #elif cond2 is different from #if cond1 . #else followed by #if cond2 because if cond1 is true, the second #if is skipped and cond2 does not need to be well-formed, while #elif ‘s cond2 must be a valid expression. As of CWG 1955, #elif that leads the skipped code block is also skipped.

[edit] Combined directives

Checks if the identifier was defined as a macro name.

#ifdef   identifier is essentially equivalent to #if defined   identifier .

#ifndef   identifier is essentially equivalent to #if !defined   identifier .

#elifdef   identifier is essentially equivalent to #elif defined   identifier .

#elifndef   identifier is essentially equivalent to #elif !defined   identifier .

[edit] Notes

While #elifdef and #elifndef directives target C++23, implementations are encouraged to backport them to the older language modes as conforming extensions.

Похожие статьи