1

我为此寻找了两天的答案,但没有成功。我以前从未遇到过这个问题,所以我会尽力而为。请多多包涵。

我回到了我一年多前创建的一个 C++ 项目,当时该项目运行没有问题。前几天,当我试图让相同的程序运行时,我遇到了这个有趣且令人难以置信的烦人问题。代码类似于:

文件.h

...
short id;
...

文件.cc

id = 0;
while (id < some_large_number)
{
   id = foo();  
   if (id == 2)
   {
      //do something
   }
   else if (id == 2900)
   {
      //do something
   }
   else if (id == 30000)
   {
      //do something
   }
   else if (id == 40000)
   {
      //do something
   }
   else if (id == 45000)
   {
      //do something
   }
   else
   {
      //do something else
   }
}

常数是我为本示例扩展的十六进制表示法的宏。事实证明,这确实是一个错误,但调试器并不容易发现。以下是发生的事情:

当我尝试使用 GDB 单步执行代码(没有优化)时,我注意到 GDBif (id == 30000)每次到达后都会直接跳转到 else 语句。因为数字是十六进制的 c 宏,所以我一开始并没有注意到它40000超出了 a 的限制signed short。这非常具有误导性,并且花了几个小时试图弄清楚:我重新编译了外部库,重新安装了 g++,等等。

显然,解决idunsigned short这个问题。另一个问题似乎是编译器问题。但是我还是不明白,为什么在执行过程中完全跳过了那些代码段,并且没有优化?为什么它不通过每个if陈述,这样我就可以确定真正的问题?有任何想法吗?

非常感谢。我希望这对于第一个问题来说没问题。

4

5 回答 5

3

如果你启用了来自 gcc 的所有警告,它会在编译时告诉你这将会发生。

于 2011-01-22T20:10:09.443 回答
1

我的结论和你的一样:即使没有打开“优化”,它似乎也被优化了。也许这些常量谓词“始终为真”/“始终为假”用于在代码生成步骤中直接跳过某处的代码,即比执行任何 -O 开关优化要快得多。只是一个猜测。

于 2011-01-22T20:14:17.247 回答
1

GCC 是一个出色的优化编译器,但是即使通过 -Werror、-Wall 等启用错误和警告信息,GCC 仍然不会产生与诊断编译相同级别的信息。在开发代码时,我建议使用诊断编译器 Clang 来帮助查找错误和错误。Clang 旨在与 GCC 兼容,除了一些更深奥的功能之外,我在 Makefile 中的两者之间更改我的 CC 没有问题。

作为一个优化编译器,我相信 GCC 默认启用死代码消除。这将导致编译器检测到不可能的所有分支(例如 id 变量范围之外的分支)被消除。您也许可以禁用这种类型的死代码消除。

于 2011-01-22T20:21:41.880 回答
1

回想一下,在编译期间,C++ 编译器会查看代码并确定代码的哪些部分以什么顺序执行。如果编译器确定部分代码永远不会运行,则不会对其进行优化。

例如:

int i = 0;
if( i == 1) {
    printf("This will never be printed\n");
}

在这里,没有理由优化 if 语句,因为它永远不会执行。

如果您使用以下代码进行编译,则会拾取此类实例:

g++ -Wall mycode.c

其中-Wall表示显示所有警告,并且mycode.c是您的项目文件。

至于执行,单步执行 GDB 显示程序的当前流程。如果一个分支(在 if 语句中)是假的,为什么它会通过代码的那部分?在 if-elseif-else 语句中只能采用一个分支。

我希望这可以帮助你。

于 2011-01-22T20:27:25.003 回答
1

short is 16 bits long and its range is -32768 to 32767. So it can never be 40000 or 45000 and compiler eliminated dead code (as it will never be reached).

于 2011-01-22T20:31:22.763 回答