【问题标题】:Can objects be unwinded before they are created on stack?对象在堆栈上创建之前可以展开吗?
【发布时间】:2010-02-24 13:20:46
【问题描述】:

我们这几天一直在调试一个奇怪的案例,并在一定程度上隔离了这个错误,但它仍然没有任何意义。也许这里的任何人都可以给我一个关于发生了什么的线索。

问题是发生在部分代码中的访问冲突。

基本上我们有这样的东西:

void aclass::somefunc() {
  try {
    erroneous_member_function(*someptr);
  } 
  catch (AnException) {
  }
}

void aclass::erroneous_member_function(const SomeObject& ref) {
  // { //<--scope here error goes away
  LargeObject obj = Singleton()->Object.someLargeObj; //<-remove this error goes away

  //DummyDestruct dummy1//<-- this is not destroyed before the unreachable

  throw AnException();

  // } //<--end scope here error goes away 

  UnreachableClass unreachable; //<- remove this, and the error goes away

  DummyDestruct dummy2; //<- destructor of this object is called! 
}

在调试器中,它实际上看起来像是在破坏 UnreachableClass,当我插入 DummyDestruct 对象时,它不会在调用奇怪的析构函数之前被破坏。因此,LargeObject 的破坏似乎并没有出错。

所有这些都在生产代码的中间,很难将其隔离为一个小示例。

我的问题是,有没有人知道是什么原因造成的,以及发生了什么?我有一个功能齐全的调试器(Embarcadero RAD 工作室),但现在我不知道如何处理它。

谁能给我一些关于如何进行的建议?

更新:

我在 throw 子句下方放置了一个 DummyDestruct 对象,并在析构函数中放置了一个断点。输入了这个对象的析构函数(只有我们在这段代码中)。

【问题讨论】:

  • 如果显式调用不可达的构造函数怎么办?我认为局部变量在它声明的范围开始时被“初始化”,除非使用了一些构造函数。
  • 你得到什么错误?哪条线导致它?
  • 你在编译什么优化?
  • @phtrivier 我遇到了访问冲突,它不会发生在任何行上,并且堆栈展开确实会留下很多调用堆栈来说明错误实际发生的位置。我可以看到实际上是一个字符串被破坏,但字符串的位置似乎在堆栈“上方”,在内存中当前未用于任何事情。 @bill 我不使用任何优化标志,例如 -O 或 -O2 但我不知道 ide 在编译时设置的每个标志。
  • 您是否打算在堆栈上制作 LargeObject 的副本,LargeObject 有多大?它会比堆栈大吗?我还认为,当您说错误消失时,我认为实际上错误仍然存​​在,但是通过好(或坏)运气,在这些情况下不会导致立即崩溃。

标签: c++ debugging c++builder access-violation


【解决方案1】:

根据您提供的信息,如果一切都如您所说,唯一可能的答案是编译器/优化器中的错误。只需添加带有注释的额外范围(这也是,如果一切都与您所说的完全一样)。

【讨论】:

  • 我也有同样的想法,但我不能接受。如果编译器中有这样的严重错误,它会被注意到。 (我们不是世界上唯一一家使用密件抄送的公司吗?)。情况就像提供的那样,但正如我所说,这是一个大型项目,很多事情可能在早期就出错了。覆盖各种内存,复制的大对象可能完全无效/被覆盖。
  • Singleton() 内存是否有可能被覆盖/弄乱?有人在扩展单例吗?
  • @daramarak:我们不是世界上唯一一家使用 BCC 的公司,对吧?我从未见过或听说过使用 BCC(在欧洲)。它是 MSVS 或 GCC。
  • @just 某人:我知道!我们是世界上唯一使用它的人,这解释了很多:)
  • @phtrivier:单例函数,访问函数内部的静态变量,不能扩展,只能由该函数构造。所以我看不出这是可能的。
【解决方案2】:

由于通过未初始化的指针写入、超出范围的数组访问等,有时会发生这种情况。导致错误的点可能会从它出现的地方完全移除。但是,根据您描述的症状,它似乎已定位在此功能中。 LargeObject 的复制构造函数可能行为不端吗?正在使用 ref 吗?也许somePtr 没有指向有效的SomeObjectSingleton() 是否返回指向有效对象的指针? 编译器错误也是可能的,尤其是在开启积极优化的情况下。我会尝试重新创建没有优化的错误。

【讨论】:

  • LargeObject 没有编译器提供的构造函数或析构函数。单例返回一个有效对象的副本(但它可能已损坏)。但是我看不到损坏的代码在这段代码中是如何做错事的,尤其是当它看起来甚至没有被创建,只是放在堆栈上时。
  • 对于Singleton() 返回的类型,-&gt; 是否重载?如果是这样,它可能会导致问题。
  • AnException的构造函数呢?顺便说一句,这是合法代码吗?根据大写字母,我猜AnException 是类名而不是对象。
  • Ari: AnException 是构造函数,是的,错字。我修好了它。是的,异常的构造函数可能是罪魁祸首。我看过代码。它不是很复杂,但也不是微不足道的。我可能会再看看它。
  • 您是否尝试过 Ari 的建议在关闭优化的情况下重新创建?
【解决方案3】:

是时候练习我的心灵感应调试技能了:

我最好的猜测是您的应用程序存在堆栈损坏错误。这可能会在调用堆栈上写入垃圾,这意味着调试器在您中断时错误地报告了该函数,并且它实际上不在析构函数中。要么是这样,要么是你错误地解释了调试器的信息,而对象确实被正确地破坏了,但你不知道为什么!

如果出现堆栈损坏,您将很难找出根本原因。这就是为什么在整个程序中实现大量诊断(例如断言)非常重要的原因,这样您就可以在堆栈损坏发生时发现它,而不是陷入其奇怪的副作用。

【讨论】:

  • 堆栈损坏是我一直遵循的假设。我一直在检查异常前后的堆栈,寻找在创建的对象中抛出析构函数,甚至通过 CPU 视图跟踪代码,以查找是否有异常行为。但是你建议当我单步执行代码时整个调试器都在欺骗我,而我看到的被调用的函数真的根本没有被调用?
  • 不,我不会走那么远......我的意思是如果堆栈损坏已经发生,调试器可能会在调用堆栈中显示不正确的信息。
  • 我不仅仅依赖于调用堆栈,我一直在单步执行代码,所以我想我至少可以相信在哪里运行什么不运行。
  • 我的问题是:如果堆栈损坏。它必须在从 try 范围到 throw 调用的某个地方损坏。所有其他损坏不应该与堆栈展开混淆。如果堆栈被损坏,它应该是由范围内的代码引起的。这是对单例和一些复制构造函数的调用,对吗?复制构造函数由编译器提供,单例调用很简单。然后我们有析构函数,如果堆栈以我认为的方式展开(最后声明首先被销毁),它甚至不会被调用。还是我的假设错了?
  • 我不知道——心灵感应调试有点难。我认为你不太可能像这样解决你的问题,因为“奇怪,无法解释的事情正在发生,为什么?”是一个很难回答的问题。我将专注于答案的最后一部分:“这就是为什么在整个程序中实施大量诊断(例如断言)很重要的原因,这样您就可以在堆栈损坏发生时捕获它,而不是陷入其怪异的一面效果。”
【解决方案4】:

这可能是一个真正的长镜头,但无论如何我都会把它放在那里......

你说你使用 borland - 什么版本?你说你在一个字符串中看到错误 - STL?你的项目中是否包含了winsock2?

我问的原因是我在使用 borland 6 (2002) 和 winsock 时遇到了问题 - 标题似乎弄乱了结构打包,意味着不同的翻译单元对 std::string 的内存布局有不同的想法,取决于翻译单元包含哪些标题,可预见的灾难性结果。

【讨论】:

  • 如果是这样的问题,我不会感到惊讶。但是很抱歉,我们的代码中没有使用winsock2。
【解决方案5】:

这是另一个疯狂的猜测,因为您提到了字符串。我知道至少一种实现,其中(STL)字符串复制是以惰性方式完成的(即,在进行更改之前不会实际复制字符串内容;“复制”是通过简单地拥有目标字符串对象来完成的指向与源相同的缓冲区)。在那个特定的实现(GNU)中存在一个错误,即过度复制会导致引用计数器(有多少对象在复制后使用相同的实际字符串内存)翻转到 0,从而导致各种恶作剧。我自己没有遇到过这个错误,但是有人告诉过我。 (我这样说是因为有人会认为 ref 计数器将是一个 32 位数字,并且至少可以说,它翻转的可能性非常小,所以我可能没有正确描述这个问题。)

【讨论】:

  • 听起来这不是我们的问题。特别是因为 deallocated 的字符串似乎位于堆栈的未使用部分,高于所有已在范围内分配的堆栈对象。
猜你喜欢
  • 1970-01-01
  • 2023-02-14
  • 2012-01-23
  • 2014-10-23
  • 1970-01-01
  • 2010-12-08
  • 2019-09-19
  • 2021-06-25
  • 1970-01-01
相关资源
最近更新 更多