【问题标题】:Access violation exception when calling a method调用方法时访问冲突异常
【发布时间】:2023-03-14 18:17:02
【问题描述】:

我在这里遇到了一个奇怪的问题。假设我有一个带有一些 virtual 方法的类。在某些情况下,此类的实例应调用其中一种方法。大多数时候那个阶段没有问题,但有时发现无法调用虚拟方法,因为指向该方法的指针为NULL(如VS所示),因此发生内存访问冲突异常。怎么会这样?

应用程序相当大且复杂,所以我真的不知道是什么低级步骤导致了这种情况。发布原始代码没有用处。

UPD:好的,我看到我对问题的描述是相当不确定的,所以示意性代码看起来像

void MyClass::FirstMethod() const { /* Do stuff */ }
void MyClass::SecondMethod() const
{
    // This is where exception occurs, 
    // description of this method during runtime in VS looks like 0x000000
    FirstMethod(); 
}

不涉及构造函数或析构函数。

【问题讨论】:

  • 您确定您使用有效的指针或引用调用这些虚拟方法吗?在调用此类方法之前,是否有可能使您的指针无效/删除?这就是我开始寻找的地方,至少没有任何示例代码......
  • @zdawg,当应用程序崩溃时,对象的this-pointer有效,但属于该对象的问题方法的指针为空。
  • 当您在调试器中时 - this 指针的计算结果是什么?空?
  • @tim,不,这个指针被正常评估,它有很多字段也被正常评估。
  • NULL 或以其他方式损坏 this* 通常不会引发异常,除非/除非访问成员变量。调用成员函数时不会取消引用。回到主题,当我得到一个异常并且不清楚是什么导致它时,我要做的第一件事就是查看崩溃位置的程序集。

标签: c++ access-violation


【解决方案1】:

堆损坏是一个可能的候选者。对象中的 v-table 指针是易受攻击的,它通常是对象中的第一个字段。碰巧与该对象相邻的某种其他对象的缓冲区溢出将擦除 v-table 指针。对虚拟方法的调用,通常会在很久以后才会发生。

另一个经典案例是“this”指针错误,通常为 NULL 或低值。当您调用该方法的对象引用不正确时,就会发生这种情况。该方法将照常运行,但一旦尝试访问类成员就会崩溃。同样,堆损坏或使用已删除的指针会导致此问题。祝你调试好运;这绝非易事。

【讨论】:

  • 问题是我无法调试这个。奇怪,但是当我在VS中以DEBUG模式运行应用程序时不会出现异常。
  • 是的,堆损坏就是这样。 v-table 指针被破坏的可能性要小得多,调试分配器倾向于在块周围放置警卫。 VS 有 可以帮你定位这类问题。
  • 谢谢,对象引用似乎已损坏。我无法弄清楚它究竟是如何发生的,但重新编写一些与获取此引用相关的代码似乎可以修复此错误。
  • 永远不容易? Valgrind 几乎总是在几分钟内向我指出这些问题。
【解决方案2】:

您可能是从本身不具有该功能的基类的构造函数调用该函数(直接或间接)。

可能某处存在损坏的类型转换(例如涉及多重继承时指针的reinterpret_cast),并且您正在查看错误类的 vtable。

您可能(但不太可能)以某种方式破坏了 vtable。

指向函数的指针是只针对这个对象,还是针对所有其他相同类型的对象?如果是前者,那么 vtable 指针就坏了,你找错地方了。如果是后者,则 vtable 本身已损坏。

【讨论】:

  • 同意。 reinterpret_cast 肯定会破坏多继承场景中的 vtable。
【解决方案3】:

这可能发生的一种情况是,如果您尝试在析构函数或构造函数中调用纯虚方法。此时该方法的虚拟表指针可能未初始化导致崩溃。

【讨论】:

  • 嗯,我刚刚意识到该方法甚至不是虚拟的。它不在构造函数或析构函数中调用,它只是一个字段的访问器,在屏幕重绘过程中调用。
  • @Tony:根据您刚刚发表的评论,听起来您正在使用NULL(或其他无效)的对象指针调用该方法。编译器将能够使用 NULL 对象指针进行函数调用,因为它是可以在编译/链接时静态解析的非虚拟调用。但是,当函数尝试实际访问类成员时,它会尝试通过无效的对象指针来访问。
【解决方案4】:

是否有可能在 SecondMethod 的处理过程中“this”指针被删除?

另一种可能性是 SecondMethod 实际上是在前面使用无效指针调用的,并且它恰好工作(通过未定义的行为)直到嵌套函数调用然后失败。如果您能够添加打印代码,请检查“this”和/或其他正在使用的指针在执行这些方法期间的各个点是否类似于 0xcdcdcdcd 或 0xfdfdfdfd。这些值(我相信)被 VS 用于内存分配/释放,这可能是它在调试模式下编译时工作的原因。

【讨论】:

    【解决方案5】:

    您最有可能看到的是实际问题的副作用。很可能是堆或内存损坏,或者引用了以前释放的对象或空指针。

    如果您可以始终让它在同一个地方崩溃并且可以确定从哪里加载空指针,那么我建议使用调试器并在触发断点后在该内存位置的“写入”上放置一个断点那么很可能您正在查看实际导致损坏的代码。

    【讨论】:

    • 我刚刚注意到关于调试模式没有崩溃的评论(在 nobugz 响应中)。你仍然可以在发布模式下调试它,但这会有点棘手,但仍然可行,这取决于你对调试器和符号表的熟练程度。
    【解决方案6】:

    如果仅在 Studio 无法显示方法地址时发生内存访问冲突,则可能是由于缺少调试信息造成的。您可能正在调试使用发布(非调试)编译器/链接器标志编译的代码。

    尝试在项目的 C++ 属性中启用一些调试信息,重建并重新启动调试器。如果有帮助,您将看到所有正常的可追踪事物,如堆栈、变量等。

    【讨论】:

      【解决方案7】:

      如果您的 this 指针为 NULL,则不太可能发生损坏。除非你正在清零你不应该有的内存。

      您没有说您是在调试 Debug(未优化)还是 Release(优化)构建。通常,如果不需要,在 Release 构建优化器将删除此指针。因此,如果您正在调试优化构建,则将此指针视为 0 并没有任何意义。您必须依靠反汇编来告诉您发生了什么。如果您无法在调试版本中重现问题,请尝试在发布版本中关闭优化。调试优化构建时,您调试的是程序集而不是 C++。

      如果您已经在调试未优化的构建,请确保在花费太多时间调试损坏的映像之前进行干净的重建。调试版本通常以增量方式链接,并且已知增量链接器会产生此类问题。如果您正在使用 clean build 运行 Debug build 并且仍然无法找出问题所在,请发布堆栈转储和更多代码。我相信我们可以帮助您解决问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多