当X未定义时,为什么没有“#if X”警告?

时间:2009-10-29 13:42:26

标签: c++ visual-studio-2008 c-preprocessor

我偶尔会编写类似这样的代码:

// 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.当X未定义时,为什么#if X不可疑?

还有一个问题:如果我在其他地方犯了同样的错误,是否有任何系统的方法可以找出?这个项目很大。

编辑:我能理解#ifdef没有任何警告是完全合理的。但肯定#if是不同的。

8 个答案:

答案 0 :(得分:29)

gcc可以为此生成警告,但标准可能不需要它:

-Wundef
如果在`#if'指令中评估未定义的标识符,则发出警告。

答案 1 :(得分:17)

同样,正如经常发生的那样,“为什么”问题的答案只是:它是以这种方式完成的,因为前段时间它决定这样做。当您在#if中使用未定义的宏时,它将被替换为0.您想知道它是否实际定义 - 使用defined()指令。

虽然“默认为0”方法有一些有趣的好处。特别是当您使用的平台可能定义的宏时,而不是您自己的宏。

例如,某些平台提供宏__BYTE_ORDER__LITTLE_ENDIAN__BIG_ENDIAN来确定其字节序。您可以编写预处理器指令,如

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

但是如果你试图在一个根本没有定义这些非标准宏的平台上编译这个代码(即对它们一无所知),上面的代码将被预处理器翻译成

#if 0 == 0
...

并且代码的little-endian版本将“默认”编译。如果您将原始#if写为

#if __BYTE_ORDER == __BIG_ENDIAN
...

然后代码的big-endian版本将“默认”编译。

我不能说#if被定义为专门用于上述技巧,但它有时很有用。

答案 2 :(得分:1)

当你不能使用带有警告信息的编译器(比如gcc中的-Wundef)时,我发现了一种生成编译器错误的有用方法。

你当然可以写:

#ifndef DO_THIS
    error
#endif
#if DO_THIS

但那真的很烦人

稍微不那么讨厌的方法是:

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

如果DO_THIS未定义,则会产生除零错误。这种方法并不理想,因为标识符拼写了两次,而第二种拼写错误会让我们回到我们开始的地方。它看起来也很奇怪。似乎应该有一种更清洁的方法来实现这一点,例如:

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

但这实际上并不奏效。

答案 3 :(得分:1)

如果您迫切希望防止出现此类错误,请尝试使用预处理器令牌粘贴魔术和表达式评估来强制定义宏(在此示例中为0):

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

答案 4 :(得分:1)

有递归问题。如果你有

#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 

答案 5 :(得分:1)

如果DO_THIS是您的定义,那么简单可行的解决方案似乎是使用类似于函数的宏:

#define DO_THIS() 1

#if DO_THIS()
    //stuff
#endif

我在Visual Studio 2008、2015和GCC v7.1.1下对此进行了测试。 如果DO_THIS()未定义,则VS生成:

  

警告C4067:预处理指令后出现意外的令牌-预期换行

GCC生成

  

错误:令牌“(”之前缺少二进制运算符

答案 6 :(得分:0)

编译器没有生成警告,因为这是一个预处理器指令。它在编译器看到它之前进行了评估和解决。

答案 7 :(得分:0)

如果我正确地考虑这个问题。

在编译任何源代码之前处理预处理程序指令。在发生这种情况的转换阶段,处理所有预处理器指令,宏等,然后编译实际的源代码。

由于#if用于确定是否已定义X,并且如果已定义或尚未定义,则执行某些操作。代码片段中的#if将编译时没有任何错误,因为就编译器而言,没有任何错误。您始终可以创建一个包含应用程序所需的特定#defines的头文件,然后包含该头文件。