C++虚表地址和虚函数地址
C++虚表地址和虚函数地址
虚函数是c++实现多态性的一个重要知识点,本文主要解析虚函数在内存中的地址,以及与虚表地址的关系。
声明一个类,包含两个虚函数,一个普通成员函数和一个类成员。
调试程序,查看test对象的内存分布如下:
_vptr是一个指向虚表的指针,可以理解为一个int* p[2],在本例中,p[0]是指向Test::func1()地址的指针,p[1]是指向Test::func2()地址的指针。那么可以直接通过p值来调用这两个虚函数:
*(p[0])() 输出Test::func1
*(p[1])() 输出Test::func1
而_vptr又总是存在于类内存空间的首位,可以通过对test取地址得到_vptr,然后再进行上述操作,代码验证如下:
对上述地址操作做一个简单地说明:
&test是获取到类实例对象的地址,
(intptr_t*)&test)将该地址转为整数型指针,再对其进行取值操作,得到指向指向虚函数表的指针的地址(这里不是打错字,请注意断句)。
((intptr_t)&test)是指向虚函数表的指针值,那么虚函数表的地址值就是*(intptr_t*)((intptr_t)&test),取虚函数表的地址的前4/8个字节,就是虚函数表中的第一个虚函数地址。强转为函数指针,也就是p1,从监视窗口可以看到,p1的地址确实是Test::func1()的地址。
(intptr_t*)((intptr_t)&test再进行+1操作(此处指针类型为intptr_t,那么+1就是偏移4或者8个字节),得到p2,也就是是Test::func2()的地址。
既然我们都可以取到指向虚函数表的指针,同样的我们就可以“修改”一个类对象的虚函数表,这里用引号是因为一个类的虚函数表是不能被修改的,我们只能修改指向虚函数表的指针,把它骗过去。。。代码如下:
class Base
{
public:
virtual void func1() = 0;
virtual void func2() = 0;
};
class Test1 : public Base
{
public:
virtual void func1()
{
std::cout << __FUNCTION__ << std::endl;
}
virtual void func2()
{
std::cout << __FUNCTION__ << std::endl;
}
};
class Test2 : public Base
{
public:
virtual void func1()
{
std::cout << __FUNCTION__ << std::endl;
}
virtual void func2()
{
std::cout << __FUNCTION__ << std::endl;
}
};
typedef void (*FuncPtr)();
int main()
{
Test1 test1;
Test2 test2;
Base *pBase = &test1;
printf("%p\n", *(intptr_t*)&test1);
printf("%p\n", *(intptr_t*)*(intptr_t*)&test1);
//FuncPtr p1 = (FuncPtr)(intptr_t*)(*(intptr_t*)*(intptr_t*)&test1);
//FuncPtr p2 = (FuncPtr)(intptr_t*)(*((intptr_t*)*(intptr_t*)&test2 + 1));
intptr_t p[] = {
((intptr_t*)*(intptr_t*)&test1)[0],
((intptr_t*)*(intptr_t*)&test2)[1],
};
*((intptr_t**)pBase) = p;
pBase->func1();
pBase->func2();
}
输出结果: