19

如何防止 GCC 消除if(0)块中的代码?

当我使用 Visual Studio 时,我的调试技术之一是将这样的代码放入我的程序中:

if (0)
    do_some_debug_printing_and_checking();

然后,当遇到断点时,我单击do_some_debug_printing_and_checking()行,选择“设置下一条语句”并强制执行。

当我使用 gcc/gdb 作为后端时,“set next statement”不再起作用,因为 GCC 只是从if(0)语句中删除了代码。

我当然使用-O0标志来禁用优化。我还尝试了-fno-dce -fno-tree-dce标志来明确禁用死代码消除,但它没有效果:二进制文件中不存在if(0)的内容,我不能使用set next声明跳进去。

有什么好方法可以告诉 gcc 禁用if(0)内容的消除?

编辑:

感谢“附加变量”的解决方法,但是有两点我不喜欢它:

  1. 它仍然是一行额外的代码
  2. 当我构建发布版本并且确实希望那些调试内容消失时,它不会自动优化。当然我可以使用#ifdef-s,但那是更多的额外行。

真的,绝对没有选择让 GCC 保留死代码?

4

2 回答 2

12

最简单的做法是使检查依赖于(例如)具有外部链接的变量。

例如

extern bool debug;
if (debug)
    do_some_debug_printing_and_checking();

命名空间范围内的某处:

bool debug = false;
于 2012-07-01T11:19:31.500 回答
8

我不会依赖 gcc 编译器标志来做到这一点。编译器标志可以在 gcc 版本之间更改,并且在编译器之间进行更改。您可能会发现自己需要在六个月内使用 Visual C++ 调试相同的代码...

@CharlesBailey 对如何使用extern变量提出了一个很好的建议。这是一种不需要将变量公开给整个模块或保存在静态存储中的替代方法。

volatile在语句范围内声明一个临时变量if

if (volatile bool dbg = false)
{
  do_some_debug_printing_and_checking();
}

这使临时变量的范围非常狭窄。volatile限定符不会让编译器对变量进行任何假设,也不会优化分支。

要记住的一件事是变量总是在堆栈上分配,并且将一直保留在堆栈上,直到函数退出。这种方法和extern方法都应该有效,但权衡取舍略有不同(并且可能可以忽略不计)。

如果您愿意使用宏来帮助解决此问题,那么您可以在将代码发布到生产环境时轻松禁用临时变量:

#ifndef IS_DEBUGGING
#  define IS_DEBUGGING 0
#endif

#if IS_DEBUGGING
#  define TMP_DBG_FLAG volatile bool dbg_flag = false
#else
#  define TMP_DBG_FLAG false
#endif

然后将您的if声明声明为:

if ( TMP_DBG_FLAG )
{
  do_some_debug_printing_and_checking();
}

当您将 IS_DEBUGGING 定义为 1 时,将创建、声明为 volatile 并保留局部变量。当您将 IS_DEBUGGING 定义为 0 时,宏扩展为常量false,编译器优化分支。也可以为该extern方法做一些非常相似的事情。

这几行额外的代码,但它们与您使用 TMP_DBG_FLAG 的次数无关。该代码也比使用大量ifdefs 更具可读性。可以使宏更安全一些(通过将值附加__LINE__到它),但这需要三个宏,并且可能不是必需的:

#if IS_DEBUGGING
// paste symbols 'x' and 'y' together
#  define TMP_DBG_FLAG_SYMCAT0(x,y) x ## y

// need one level of indirection to expand __LINE__...
#  define TMP_DBG_FLAG_SYMCAT(x,y) TMP_DBG_FLAG_SYMCAT0(x,y)

#  define TMP_DBG_FLAG volatile bool TMP_DBG_FLAG_SYMCAT(dbg_flag_,__LINE__) = false
#else
#  define TMP_DBG_FLAG false
#endif
于 2012-07-01T19:38:31.627 回答