Почему нет предупреждения с "#if X", когда X undefined?

Я иногда пишу код примерно так:

// file1.cpp
#define DO_THIS 1

#if DO_THIS
    // stuff
#endif

Во время разработки кода я могу переключить определение DO_THIS между 0 и 1.

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

// file1.cpp
#define DO_THIS 1

и

// file2.cpp
#if DO_THIS
    // stuff
#endif

Очевидно, я исправил ошибку, но потом подумал: почему компилятор не предупредил меня? У меня установлен уровень предупреждения 4. Почему не подозрительно #if X, когда X не определен?

Еще один вопрос: есть ли какой-либо систематический способ узнать, совершил ли я ту же ошибку в другом месте? Проект огромен.

EDIT: я понимаю, что без предупреждения С#ifdef это имеет смысл. Но, конечно, #if отличается.

Ответ 1

gcc может генерировать предупреждение для этого, но его, вероятно, не требуется по стандарту:

-Wundef
Предупреждать, если идентификатор undefined оценивается в директиве `#if '.

Ответ 2

Опять же, как это часто бывает, ответ на вопрос "почему" справедлив: так было сделано, потому что некоторое время назад было решено сделать это таким образом. Когда вы используете макрос undefined в #if, он заменяется на 0. Вы хотите знать, действительно ли оно определено - используйте директиву defined().

Есть некоторые интересные преимущества для этого подхода "по умолчанию 0". Особенно, когда вы используете макросы, которые могут быть определены платформой, а не ваши собственные макросы.

Например, некоторые платформы предлагают макросы __BYTE_ORDER, __LITTLE_ENDIAN и __BIG_ENDIAN для определения их соответствия. Вы можете написать директиву препроцессора, например

#if __BYTE_ORDER == __LITTLE_ENDIAN
  /* whatever */
#else
  /* whatever */
#endif

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

#if 0 == 0
...

а версия кода младшего порядка будет скомпилирована "по умолчанию". Если вы написали оригинальный #if как

#if __BYTE_ORDER == __BIG_ENDIAN
...

тогда большая версия кода будет скомпилирована "по умолчанию".

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

Ответ 3

Компилятор не генерировал предупреждение, потому что это директива препроцессора. Он оценил и разрешил до того, как компилятор увидит его.

Ответ 4

Если вы не можете использовать компилятор с предупреждающим сообщением (например, -Wundef in gcc), я нашел один из полезных способов генерации ошибок компилятора.

Конечно, вы всегда можете написать:

#ifndef DO_THIS
    error
#endif
#if DO_THIS

Но это действительно раздражает

Несколько менее раздражающий метод:

#if (1/defined(DO_THIS) && DO_THIS)

Это приведет к делению на нулевую ошибку, если DO_THIS - undefined. Этот метод не идеален, потому что идентификатор выдается дважды, а второй ордер во второй раз вернет нас туда, где мы начали. Это выглядит странно. Кажется, должен быть более чистый способ сделать это, например:

#define PREDEFINED(x) ((1/defined(x)) * x)
#if PREDEFINED(DO_THIS)

но это фактически не работает.

Ответ 5

Если вы отчаянно пытаетесь предотвратить такую ​​ошибку, попробуйте следующее, в котором используется проверка маркера и прокрутки маркера в препроцессоре для обеспечения того, чтобы макрос был определен (и 0 в этом примере):

#define DEFINED_VALUE(x,y) (defined (y##x) ? x : 1/x)
#if DEFINED_VALUE(FEATURE1,) == 0

Ответ 6

Рекурсивная проблема. Если у вас есть

#define MODEL  MODEL_A

#if (MODEL == MODEL_B)
 // Surprise, this is compiled!
#endif

где отсутствуют определения MODEL_A и MODEL_B, тогда он будет компилироваться.

#ifdef MODEL
#error Sorry, MODEL Not Defined
// Surprise, this error is never reached (MODEL was defined by undefined symbol!)
#endif 

#ifdef MODEL_B
#error Sorry, MODEL_B Not Defined
// This error is reached
#endif 

Ответ 7

Если я думаю об этом правильно.

Директивы препроцессора обрабатываются до того, как будет скомпилирован исходный код. Во время этой фазы (ов) перевода, в которой это происходит, все директивы препроцессора, макросы и т.д. Обрабатываются, а затем скомпилирован фактический исходный код.

Так как #if используется для определения определения X и выполнения какого-либо действия, если оно определено или не определено. #if в фрагменте кода будет компилироваться без каких-либо ошибок, потому что ошибок в компиляторе нет. Вы всегда можете создать файл заголовка с определенными #defines, которые потребуются вашему приложению, а затем включить этот заголовок.