2012-07-11 111 views
3

我正在用包含图形文件解析器的C++/Qt编写程序。我使用g++编译该项目。在开发过程中,我经常比较不同编译器标志之间关于优化和调试信息以及Qt调试标志(打开/关闭qDebug()和Q_ASSERT())的低级别解析器层的性能。编译器优化使程序崩溃

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

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

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

如果你能告诉我哪种编译器优化方法可能成为这类问题的候选者,你可以帮助我很多。

+3

听起来像某处的未定义的行为。 – 2012-07-11 00:17:28

+0

@ R.MartinhoFernandes是的,但我怎么找到它?也许使用valgrind? – leemes 2012-07-11 00:18:08

+1

是的,valgrind会警告你未经初始化的值。 – MatijaSh 2012-07-11 00:22:32

回答

15

在优化版本中通常会出现一些类错误,这些类通常不会出现在调试版本中。

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

  2. 使用超出范围的临时设备。例如,当您返回一个函数中的本地临时引用时。这些通常在调试版本中工作,因为堆栈被填充得更多。临时人员倾向于在堆叠上存活一段时间。

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

+1

非常感谢。这不仅适用于这种情况,而且适用于未来的情况,当我再次遇到这样的问题时! – leemes 2012-07-11 00:45:44

1

1)在破损版本上使用valgrind。 (对于这个问题,可以尝试在工作版本上使用valgrind,也许你会幸运的。)

2)使用“-O1 -g”构建系统,并使用gdb执行程序。在碰撞时,哪个变量具有不正确的值?重新运行你的程序并记录该变量被写入的时间(或者当它不是,本应该是的时候)。

2

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

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

这应该让你在调试器中的代码更容易跟踪。

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

+0

谢谢。 Q_ASSERT就是这样一个宏,这取决于'#define'的存在。宏在发布构建模式下被禁用(对编译器使用-D标志)。 – leemes 2012-07-11 00:43:54

+0

@leemes:嘿。我只是想要添加新的跟踪,而不是已经存在的新跟踪。问候 – jxh 2012-07-11 02:10:33