【问题标题】:compiling with o2 flag makes program to trow access violation使用 o2 标志编译会使程序抛出访问冲突
【发布时间】:2011-05-26 21:11:36
【问题描述】:

我知道这可能是一生一次的问题,但我一直坚持下去,我想不出任何可能导致这个问题的问题,我用 C++ 编写了一个代码(大约 500 行单独类和文件)使用visual studio,当我在没有优化标志(/od)的情况下编译它时它工作正常,但是当我尝试使用发布配置(/o2标志用于优化)编译它时,程序会出现访问冲突和崩溃。经过一些调试后,我发现有一个this 值在一个成员函数内部发生了变化,但是如果指针发生变化,我看不到调用堆栈中指针的任何直接使用,任何人都可以给出任何建议是什么导致这种情况发生仅在启用优化时?

不知道这是否对您有帮助,但是当我使用优化进行编译时,我可以看到在我的第一个函数调用的末尾添加了一个汇编指令pop ebp不知道这是什么确实如此,但不管它是什么,这就是这个指针改变的地方。

我在尝试使用反汇编程序进行调试时发现了一些新的东西,有 13 条 push 指令,而导致问题的函数中只有 10 条 pop 指令(问题是由 @987654325 之前的最后一次弹出引起的@指令)可以吗? (我也在计算被调用的函数中的所有推送、弹出指令。)

【问题讨论】:

  • 这可能是即使在调试版本中也存在的问题,但由于内存布局和损坏的内容,它可能不会出现。我会先梳理一下你是如何管理你的记忆的。查看所有动态分配和释放。
  • 当我尝试在该函数(以及它调用的所有函数)中进行调试时,如果程序在没有优化的情况下编译,则没有内存限制,但我会仔细检查。
  • 除此之外,我在该函数中使用的每一件事都通过多个项目进行了测试(它们是我在每个项目中使用的一些基类)
  • @Gajet,通常你会因为几种类型的问题得到这个,你已经释放了一些东西并再次访问它,或者你已经访问了缓冲区的末尾(检查字符串/数组的使用等.) 在不真正了解您的代码的情况下,很难说出问题出在哪里,我们所能做的就是提供一般性提示。
  • @Gajet,就像我说的,内存布局在调试和优化模式下可能不同,所以如果你有一个缓冲区溢出,例如,在调试模式下你可能很幸运,因为内存是访问仍然可以,但在优化模式下,它可能会遇到无法读取的地址。优化是一个红鲱鱼,相信我,编译器不太可能做一些愚蠢的事情......

标签: c++ visual-studio-2010 optimization assembly access-violation


【解决方案1】:

您在优化和不优化时看到不同行为的原因是您的代码(无意中)依赖于未定义的行为。如果编译器以一种方式布局数据,它恰好可以工作,如果编译器以不同的方式布局数据,它就会中断。

换句话说,你有一个错误。

它可能存在于您已经测试过的代码中,也可能存在于您使用该代码的方式中。在任何情况下,正如@Nim 在 cmets 中所说,检查您分配和释放内存的位置。检查您的课程是否遵循三原则。验证您在某处没有缓冲区溢出。也许,也可以尝试使用不同的编译器来编译它。使用静态分析工具(MSVC 有 /analyze,Clang 有 --analyze。在 Linux 上 Valgrind 可能是一个不错的选择)。

但不要认为这是一个编译器错误。当然,这些确实会发生,但它们通常不是此类错误的根源。在几乎所有情况下,它都是开发人员自己的代码中的一个潜在错误。仅仅因为它不会每次都触发,每个编译器标志并不意味着它不存在,或者它是编译器的错。

【讨论】:

    【解决方案2】:

    既然你说 this 指针突然改变值让我相信这与堆损坏有关。另一方面,既然您说这与优化代码无关,那么它也可能与堆栈有关。优化器所做的其中一件事是,它删除了放在堆栈上的未使用的变量,这些变量永远不会被访问。

    这实际上意味着当你没有在优化模式下编译时,堆栈上会出现更多的变量,从而使内存布局有些不同,从某种意义上说给堆栈增加了更多的内存空间,这可能会产生巨大的影响。影响软件对堆栈溢出的反应。

    如果有从未使用过的局部变量,程序不会关心您是否损坏了从未使用过的局部变量的内存。只有当您损坏实际使用的内存时,它才会成为问题。

    您可以告诉编译器使用不同的警告级别(如果我没记错的话,四个级别)。如果您使用最高的警告将被视为编译器错误,这将停止编译过程。通过这种方式,您可以注意到在优化代码时将被删除的局部变量,并且可以让您更接近真正的问题。开始搜索这些代码区域。

    我也建议你剪掉代码测试一下,只是为了排除有问题的代码在哪里,逐步往下挖关闭问题。当您没有任何信息时,您必须从头开始(程序的主循环)并尝试隔离并排除运行正常的代码部分。 “如果我注释掉这个函数调用,那么它就不会崩溃”可能会给你一个提示:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-12
      • 1970-01-01
      • 2018-04-08
      • 2021-03-11
      • 2012-11-08
      • 2022-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多