#ifdef vs #if - 作为启用/禁用特定代码段编译的方法更好/更安全?

Jon*_*age 109 c c++ if-statement coding-style c-preprocessor

这可能是一种风格问题,但我们的开发团队有一点分歧,我想知道是否有其他人对此事有任何想法......

基本上,我们有一些调试打印语句,我们在正常开发过程中关闭.我个人更喜欢做以下事情:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}
Run Code Online (Sandbox Code Playgroud)

有些团队更喜欢以下内容:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}
Run Code Online (Sandbox Code Playgroud)

......哪些方法对你来说听起来更好?为什么?我的感觉是,第一个更安全,因为总有一些东西被定义,并且没有危险它可以破坏其他地方的其他定义.

Rod*_*ddy 80

#ifdef当然,我最初的反应是,但我认为#if实际上有一些显着的优势 - 这就是为什么:

首先,您可以DEBUG_ENABLED在预处理器编译测试中使用.示例 - 通常,在启用调试时我想要更长的超时,所以使用#if,我可以写这个

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);
Run Code Online (Sandbox Code Playgroud)

... 代替 ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif
Run Code Online (Sandbox Code Playgroud)

其次,如果要从a迁移#define到全局常量,则处于更好的位置.#define大多数C++程序员通常都不赞同.

而且,第三,你说你的团队存在分歧.我的猜测是,这意味着不同的成员已采用不同的方法,您需要标准化.裁决#if是首选的选择意味着代码使用#ifdef将编译 - 并运行 - 即使DEBUG_ENABLED是假的.而且它的很多容易追查并卸下时,它不应该比反之亦然生产调试输出.

哦,还有一个小的可读性点.你应该能够使用真/假而不是0/1 #define,并且因为该值是单个词法标记,所以有一次你不需要围绕它的括号.

#define DEBUG_ENABLED true
Run Code Online (Sandbox Code Playgroud)

代替

#define DEBUG_ENABLED (1)
Run Code Online (Sandbox Code Playgroud)

  • 是的,`#ifdef`的一个问题是它适用于未定义的东西; 他们是不是故意定义,还是因为拼写错误或者你有什么. (7认同)
  • 你对答案的补充是错误的.`#if DEBUG_ENBALED`不是预处理器检测到的错误.如果没有定义`DEBUG_ENBALED`,它会扩展为`#if`指令中的标记`0`. (6认同)
  • @R ..在许多编译器中,当未定义DEBUG_ENABLED时,您可以为"#if DEBUG_ENABLED"启用警告.在GCC中使用"-Wundef".在Microsoft Visual Studio中使用"/ w14668"打开C4668作为1级警告. (6认同)

R..*_*R.. 55

他们都很可怕.相反,这样做:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif
Run Code Online (Sandbox Code Playgroud)

然后,只要您需要调试代码,请将其放入D();.并且您的程序没有被可怕的迷宫污染#ifdef.

  • @MatthieuM.实际上,我觉得原版很好.分号将被解释为空语句.但是,忘记分号会使它变得危险. (6认同)
  • 谢谢.我会解决它. (2认同)

Ter*_*son 29

#ifdef 只是检查是否定义了一个令牌

#define FOO 0
Run Code Online (Sandbox Code Playgroud)

然后

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"
Run Code Online (Sandbox Code Playgroud)


Bre*_*ddy 18

我们在多个文件中遇到了同样的问题,并且总是存在人们忘记包含"功能标志"文件的问题(代码库> 41,000个文件很容易做到).

如果你有feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H
Run Code Online (Sandbox Code Playgroud)

但是你忘了在header.cpp中包含头文件:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif
Run Code Online (Sandbox Code Playgroud)

然后你有一个问题,在这种情况下,编译器将COOL_FEATURE解释为未定义为"false",并且无法包含代码.是的,gcc确实支持一个导致未定义宏错误的标志......但是大多数第三方代码要么定义要素,要么不定义要素,所以这不是那么便携.

我们采用了一种便携式方法来纠正这种情况,并测试一个功能的状态:功能宏.

如果您将上述feature.h更改为:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H
Run Code Online (Sandbox Code Playgroud)

但是你再次忘记在file.cpp中包含头文件:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif
Run Code Online (Sandbox Code Playgroud)

由于使用了未定义的函数宏,预处理器会出错.


Mik*_*son 16

出于执行条件编译的目的,#if和#ifdef 几乎相同,但并不完全相同.如果条件编译依赖于两个符号,则#ifdef也不会起作用.例如,假设您有两个条件编译符号PRO_VERSION和TRIAL_VERSION,您可能会遇到以下情况:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif
Run Code Online (Sandbox Code Playgroud)

使用#ifdef变得更加复杂,特别是让#else部分工作.

我在广泛使用条件编译的代码上工作,我们混合了#if和#ifdef.对于简单情况,我们倾向于使用#ifdef /#ifndef;对于正在评估的两个或更多符号,我们倾向于使用#if.


Der*_*ark 14

我认为这完全是一种风格问题.两者都没有明显优势.

一致性比任何一种选择都重要,因此我建议您与团队一起选择一种风格并坚持下去.


Jim*_*uck 7

我自己更喜欢:

#if defined(DEBUG_ENABLED)
Run Code Online (Sandbox Code Playgroud)

因为它更容易创建代码来查找相反的条件更容易发现:

#if !defined(DEBUG_ENABLED)
Run Code Online (Sandbox Code Playgroud)

#ifndef(DEBUG_ENABLED)
Run Code Online (Sandbox Code Playgroud)

  • 就个人而言,我认为错过这个小感叹号更容易! (8认同)
  • 语法高亮?:)在语法高亮中,"ifndef"中的"n"更难以发现,因为它的颜色都是相同的. (5认同)

Lev*_*Lev 6

这是一种风格问题.但我建议采用更简洁的方法:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);
Run Code Online (Sandbox Code Playgroud)

你这样做一次,然后总是使用debug_print()打印或什么都不做.(是的,这将在两种情况下编译.)这样,您的代码不会被预处理器指令乱码.

如果你收到警告"表达没有效果"并想要摆脱它,这里有一个替代方案:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);
Run Code Online (Sandbox Code Playgroud)

  • 也许打印宏不是最好的例子 - 我们实际上已经在我们的代码库中为我们更标准的调试代码做了这个.我们将#if/#ifdefined位用于您可能要打开额外调试的区域. (2认同)

Mar*_*ett 5

#if为您提供将其设置为 0 以关闭功能的选项,同时仍然检测到开关在那里。
就个人而言,我总是#define DEBUG 1这样我就可以用 #if 或 #ifdef 来捕捉它