Другой метод условной компиляции состоит в использовании директив #ifdef и #ifndef, что соответственно означает «если определено» и «если не определено». Стандартный вид #ifdef следующий:
#ifdef имя_макроса
последовательность операторов
#endif
Если имя макроса определено ранее в операторе #define, то последовательность операторов, стоящих между #ifdef и #endif, будет компилироваться. Стандартный вид #ifndef следующий:
#ifndef имя_макроса
последовательность операторов
#endif
Если имя макроса не определено ранее в операторе #define, то последовательность операторов, стоящих между #ifdef и #endif, будет компилироваться.
Kaк #ifdef, так и #ifndef могут использовать оператор #else, но нe #elif. Например:
#include
#define TED 10
int main(void)
<
#ifdef TED
printf(«Hi Ted
«);
#else
printf(«Hi anyone
«);
#endif
#ifndef RALPH
printf(«RALPH not defined
«);
#endif
return 0;
>
выводит «Hi Ted» и «RALPH not defined». Если TED не определен, то выведется «Hi anyone», a за ним «RALPH not defined».
#ifdef и #ifndef можно вкладывать друг в друга так же, как и #if.
Директивы #ifdef и #ifndef имеют тот же результат, что и директива #if , когда они используются с определенным оператором. The #ifdef and #ifndef directives have the same effect as the #if directive when it’s used with the defined operator.
Синтаксис Syntax
#ifdef идентификатор #ifdef identifier
#ifndef идентификатор #ifndef identifier
Эти директивы эквивалентны следующим: These directives are equivalent to:
определено #if идентификатор #if defined identifier
#if! определено идентификатор #if !defined identifier
Примечания Remarks
Можно использовать директивы #ifdef и #ifndef в любом месте #if . You can use the #ifdef and #ifndef directives anywhere #if can be used. Оператор #ifdef identifier эквивалентен #if 1 , если определен идентификатор . The #ifdef identifier statement is equivalent to #if 1 when identifier has been defined. Это эквивалентно #if 0 тому #undef , что идентификатор не был определен или был неопределен директивой. It’s equivalent to #if 0 when identifier hasn’t been defined, or has been undefined by the #undef directive. Эти директивы проверяют наличие или отсутствие только идентификаторов, определенных с директивой #define , а не идентификаторов, объявленных в исходном коде C или C++. These directives check only for the presence or absence of identifiers defined with #define , not for identifiers declared in the C or C++ source code.
Эти директивы предназначены только для совместимости с предыдущими версиями языка. These directives are provided only for compatibility with previous versions of the language. #if Предпочтительным является заданное константное выражение ( идентификатор ) , используемое с директивой. The defined( identifier ) constant expression used with the #if directive is preferred.
Директива #ifndef проверяет наличие противоположного условия, установленного #ifdef. The #ifndef directive checks for the opposite of the condition checked by #ifdef. Если идентификатор не был определен или его определение было удалено с помощью, условие имеет #undef значение true (отличное от нуля). If the identifier has not been defined, or if its definition has been removed with #undef , the condition is true (nonzero). В противном случае условие не выполняется (false, значение равно 0). Otherwise, the condition is false (0).
Блок, относящийся только к системам Microsoft Microsoft Specific
Идентификатор можно передать из командной строки с помощью параметра /d . The identifier can be passed from the command line using the /D option. С /D можно указать до 30 макросов. Up to 30 macros can be specified with /D .
Директива #ifdef полезна для проверки существования определения, так как определение может быть передано из командной строки. The #ifdef directive is useful for checking whether a definition exists, because a definition can be passed from the command line. Например: For example:
Завершение блока, относящегося только к системам Майкрософт END Microsoft Specific
Я не понимаю, то ли я криворук, то ли лыжи не скачут.
Почему компилятор ругается на LNK2005 «int QWE» (?QWE@@3HA) уже определен в A.obj, если были использованы ifndef, define, endif? Пробовал качать другую версию MSVS, пробовал писать #pragma once , но ошибка остаётся. Ткните носом, где я туплю?
2 ответа 2
Вы подключаете (пусть опосредованно) c.h в разные .cpp -файлы, вот у вас и получается в двух разных файлах определение
Вот линковщик и ругается, встречая две переменные с одним именем в двух разных местах, не зная, что ему выбрать.
Объявите переменную в .h -файле как
а в одном .cpp -файле определите
Так вы перестанете нарушать правило одного определения.

Назначение include guards #ifndef/#define/#endif — защитить ваш код от ошибок вида
т.е. от множественного включения одного и того же заголовка в одну и ту же единицу трансляции (явно или опосредованно, через другие заголовки). Такое множественное включение в общем случае приведет к ошибкам компиляции.
Include guards, вопреки вашим ожиданиям, не имеют никакого отношения к защите от ошибок множественного определения переменных или функций во всей программе, т.е. между разными единицами трансляции (вызывающих ошибки линковки). От вашей ошибки include guards никак не защищают и не должны защищать, поэтому ваше удивление «Почему компилятор ругается на LNK2005, . если были использованы ifndef, define, endif?» совершенно не оправданно.
Либо перестаньте совать определения переменных в заголовочный файл (т.е. пойдите по «классическому» пути с extern ). Либо воспользуйтесь свойством С++17 и просто сделайте вашу переменную inline