禁用gcc中的“if(0)”消除

时间:2012-07-01 11:14:34

标签: c++ optimization gcc gdb

如何阻止GCC消除 if(0)阻止内的代码?

当我使用Visual Studio时,我的一种调试技巧就是将这样的代码放在我的程序中:

if (0)
    do_some_debug_printing_and_checking();

然后,当遇到断点时,我点击 do_some_debug_printing_and_checking()行,选择“set next statement”并强制执行。

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

我当然使用 -O0 标志来禁用优化。我还尝试了 -fno-dce -fno-tree-dce 标志来明确禁用死代码消除,但它没有效果: if(0)的内容只是不存在于二进制文件中,我不能使用设置下一个语句来跳转到它。

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

编辑:

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

  1. 这仍然是一行额外的代码
  2. 当我构建发布版本并希望这些调试内容消失时,它不会自动优化。当然我可以使用#ifdef-s,但那更多的是额外的行。
  3. 真的,绝对没有选择让GCC保留死代码吗?

2 个答案:

答案 0 :(得分:12)

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

E.g。

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

命名空间范围内的某处:

bool debug = false;

答案 1 :(得分: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的次数无关。该代码比使用大量ifdef s更具可读性。可以使宏更安全(通过将__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