8

我正在用 C++/Qt 编写一个程序,其中包含一个图形文件解析器。我g++用来编译项目。

在开发过程中,我不断比较我的低级解析器层在关于优化和调试信息的不同编译器标志之间的性能,以及 Qt 的调试标志(打开/关闭 qDebug() 和 Q_ASSERT())。

现在我面临一个问题,其中唯一正常运行的构建是没有任何优化的构建。所有其他版本,即使是-O1,似乎也以另一种方式工作。它们由于不满足的断言而崩溃,在没有-O...标志的情况下编译时满足。该代码不会产生任何编译器警告,即使使用-Wall.

我很确定我的程序中有一个错误,这似乎只有在启用优化时才有害。问题是:即使在调试程序时我也找不到它。解析器似乎从文件中读取了错误的数据。当我运行一些简单的测试用例时,它们运行良好。当我运行更大的测试用例(直接从文件读取的图表上的路线计算)时,文件中的读取不正确,我无法解释。

我应该从哪里开始追踪这种未定义行为的问题?这种不同的行为可能涉及哪些优化方法?(我可以一个接一个地启用所有标志,但我不知道那么多编译器标志,但-O...我知道它们有很多,所以这需要很长时间。)一旦我知道哪种类型这个错误是,我相信我迟早会找到它。

如果您能告诉我哪些编译器优化方法可能是此类问题的候选方法,您可以帮我很多。

4

3 回答 3

22

在优化构建中通常会出现几类错误,而在调试构建中通常不会出现。

  1. 未初始化的变量。编译器可以捕获一些但不是全部。查看所有构造函数,查看全局变量。等等。特别是寻找未初始化的指针。在调试版本中,内存被重置为零,但在发布版本中不是。

  2. 使用超出范围的临时人员。例如,当您在函数中返回对本地临时的引用时。这些通常在调试版本中起作用,因为堆栈被填充得更多。临时人员往往会在堆栈上存活更长时间。

  3. 数组超出了临时对象的写入。例如,如果您在函数中创建一个临时数组,然后在末尾写入一个元素。同样,堆栈在调试中将有额外的空间(用于调试信息),并且您的溢出不会影响程序数据。

于 2012-07-11T00:41:43.073 回答
4

您可以从优化的构建中禁用一些优化,以帮助更轻松地调试优化版本。

-g -O1 -fno-inline -fno-loop-optimize -fno-if-conversion -fno-if-conversion2 \
  -fno-delayed-branch

这应该使在调试器中单步执行您的代码更容易理解。

另一个建议是,如果您的断言没有为您提供有关导致问题的足够信息,您应该考虑添加更多断言。如果您担心性能问题或断言混乱,可以将它们包装在宏中。这使您可以将调试断言与您最初添加的断言区分开来,以便以后可以从您的代码中禁用或删除它们。

于 2012-07-11T00:42:06.310 回答
3

1) 在损坏的版本上使用 valgrind。(就此而言,在工作版本上尝试 valgrind,也许你会很幸运。)

2) 使用“-O1 -g”构建系统并使用 gdb 逐步执行您的程序。崩溃时,哪个变量的值不正确?重新运行您的程序并注意何时写入该变量(或何时未写入且应该写入。)

于 2012-07-11T00:20:45.157 回答