【问题标题】:Unknown segmentation fault involving vtable lookup涉及 vtable 查找的未知分段错误
【发布时间】:2015-02-18 05:06:33
【问题描述】:

所以我搞砸了虚函数,试图找到一种方法来降低它们的成本,但我遇到了一个完全未知的错误。我的整个代码如下;

#include <iostream>
    #include <cmath>

    class Base
    {
    public:
        virtual void func() = 0;
    };

    class Der1 : public Base
    {
    public:
        virtual void func();
    };
    void Der1::func(){std::cout << "I am Der1\n";}

    class Der2 : public Base
    {
    public:
        virtual void func();
    };
    void Der2::func(){std::cout << "I am Der2\n";}

    void myFornction(Base* B){
        std::cout << "Within Fornction " << B << '\n';
        for (int i = 0; i < 10; i++)
            B->func();
    }

    using namespace std;

    int main()
    {
        Der2* B;
        std::cout << "Actual Address " << B << '\n';
        myFornction(B);
        return 0;
    }

这是使用包含在代码块中的 GCC 编译器编译的(不幸的是,该版本未列出),这是错误函数的程序集;

24  void myFornction(Base* B){
0x00401373  push   %ebp
0x00401374  mov    %esp,%ebp
0x00401376  sub    $0x28,%esp
25      std::cout << "Within Fornction " << B << '\n';
0x00401379  movl   $0x46e03a,0x4(%esp)
0x00401381  movl   $0x477860,(%esp)
0x00401388  call   0x468e68 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x0040138D  mov    0x8(%ebp),%edx
0x00401390  mov    %edx,(%esp)
0x00401393  mov    %eax,%ecx
0x00401395  call   0x449524 <std::ostream::operator<<(void const*)>
0x0040139A  sub    $0x4,%esp
0x0040139D  movl   $0xa,0x4(%esp)
0x004013A5  mov    %eax,(%esp)
0x004013A8  call   0x468f44 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)>
26      for (int i = 0; i < 10; i++)
0x004013AD  movl   $0x0,-0xc(%ebp)
0x004013B4  jmp    0x4013c5 <myFornction(Base*)+82>
0x004013C2  incl   -0xc(%ebp)
0x004013C5  cmpl   $0x9,-0xc(%ebp)
0x004013C9  setle  %al
0x004013CC  test   %al,%al
0x004013CE  jne    0x4013b6 <myFornction(Base*)+67>
27          B->func();
0x004013B6  mov    0x8(%ebp),%eax
0x004013B9  mov    (%eax),%eax
0x004013BB  mov    (%eax),%eax /// The program fails with a segmentation fault here
0x004013BD  mov    0x8(%ebp),%ecx
0x004013C0  call   *%eax
28  }
0x004013D0  leave
0x004013D1  ret

程序在运行时以分段错误退出,查看程序集我可以知道在查找期间发生分段错误,但我想不出原因或解决方案。此外,现在发生这种情况有点随机,因为我一直在使用我自己的一些虚拟课程一段时间,没有任何问题。为什么现在?为什么这个特定的代码会产生错误?

【问题讨论】:

  • 真的吗? B 指向什么?
  • 如果你不知道为什么会崩溃,你不应该考虑使用 vftable...
  • 我什至没有注意到初始化的指针
  • @user4578093 不要将答案编辑到问题中,只需接受答案就足够了:)

标签: c++ segmentation-fault virtual-functions vtable


【解决方案1】:

您应该在传递之前将B 初始化为main(),因为它不指向任何地方。

Der2* B = new Der2();

【讨论】:

  • 发现我没有眼睛的方法是什么
  • 这也不会是最后一次。相信我,我知道。
  • 我非常确定:P
【解决方案2】:

Der2* B; 是一个不指向任何地方的指针。您取消引用它会导致未定义的行为。

也许您的意思是 Der2 *B = new Der2; 或其他什么。

【讨论】:

  • 我完全错过了
【解决方案3】:

你声明了Der2* B,但没有给它赋值,所以它没有有效值。默认情况下,GCC 不会产生警告,但您通常应该使用-Wall 进行编译。如果你这样做,你会看到

tmp.cpp: In function ‘int main()’:
tmp.cpp:35:43: warning: ‘B’ is used uninitialized in this function [-Wuninitialized]
         std::cout << "Actual Address " << B << '\n';
                                           ^

使用未初始化的值是未定义的行为,对于指针,它通常会导致段错误,就像你正在经历的那样。具体来说,B-&gt;func(); 这一行。它需要取消引用 B 以获取 vptr 并找到要调用的函数。在我的机器上,B 被隐式初始化为零,所以即使取消引用它也会发出段错误的信号。

解决方案:将B 设为局部变量并将myFornction 的签名更改为

void myFornction(Base& B);

或像这样声明B

Base* B = new Der2();

顺便说一句,Base 应该有一个 virtual 析构函数。

class Base
{
public:
  virtual void func() = 0;
  virtual ~Base() { }
};

这将确保如果你在Der2 中定义了一个数据成员,当你调用delete B 时,Der2 的析构函数也会被调用。否则,只有Base 会是。

【讨论】:

  • 是的,我完全错过了整个未初始化指针的事情,看起来我在最后一次的霰弹枪调试尝试中删除了 -wall :P。它们都是空类,不包含任何数据,所以我认为析构函数是不必要的,这是不正确的吗?
  • @user4578093 通过指向具有非虚拟析构函数的基类的指针删除对象会导致未定义的行为。如果您想去那里,请注意所有赌注都已关闭
猜你喜欢
  • 2014-03-13
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-11
  • 2021-01-12
相关资源
最近更新 更多