涉及 vtable 查找的未知分段错误
Unknown segmentation fault involving vtable lookup
所以我摆弄虚函数,试图找到一种方法来降低它们的成本,但我遇到了一个完全未知的错误。我的整个代码如下;
#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 [=11=]x28,%esp
25 std::cout << "Within Fornction " << B << '\n';
0x00401379 movl [=11=]x46e03a,0x4(%esp)
0x00401381 movl [=11=]x477860,(%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 [=11=]x4,%esp
0x0040139D movl [=11=]xa,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 [=11=]x0,-0xc(%ebp)
0x004013B4 jmp 0x4013c5 <myFornction(Base*)+82>
0x004013C2 incl -0xc(%ebp)
0x004013C5 cmpl [=11=]x9,-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
程序在 运行 时退出并出现段错误,查看程序集我可以看出段错误发生在查找期间,但我想不出原因或解决方案。此外,现在发生这种情况有点随机,因为我一直在使用我自己的一些虚拟 类 一段时间,没有任何问题。为什么现在?为什么这个特定的代码会产生错误?
Der2* B;
是一个不指向任何地方的指针。您取消引用它会导致未定义的行为。
也许你的意思是 Der2 *B = new Der2;
之类的。
你应该在 main()
中初始化 B
然后再传递它,因为它没有指向任何地方。
Der2* B = new Der2();
您声明了 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->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
会。
所以我摆弄虚函数,试图找到一种方法来降低它们的成本,但我遇到了一个完全未知的错误。我的整个代码如下;
#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 [=11=]x28,%esp
25 std::cout << "Within Fornction " << B << '\n';
0x00401379 movl [=11=]x46e03a,0x4(%esp)
0x00401381 movl [=11=]x477860,(%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 [=11=]x4,%esp
0x0040139D movl [=11=]xa,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 [=11=]x0,-0xc(%ebp)
0x004013B4 jmp 0x4013c5 <myFornction(Base*)+82>
0x004013C2 incl -0xc(%ebp)
0x004013C5 cmpl [=11=]x9,-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
程序在 运行 时退出并出现段错误,查看程序集我可以看出段错误发生在查找期间,但我想不出原因或解决方案。此外,现在发生这种情况有点随机,因为我一直在使用我自己的一些虚拟 类 一段时间,没有任何问题。为什么现在?为什么这个特定的代码会产生错误?
Der2* B;
是一个不指向任何地方的指针。您取消引用它会导致未定义的行为。
也许你的意思是 Der2 *B = new Der2;
之类的。
你应该在 main()
中初始化 B
然后再传递它,因为它没有指向任何地方。
Der2* B = new Der2();
您声明了 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->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
会。