最近在做的一个项目的一个步骤找到虚函数的实际地址。这个过程中遇到的问题,让我感觉到了自己对于C++指针的理解还是不够透彻,记录一下。
概念
- 指针由两个空间组成
- 一是指针本身占有一定的空间,32位操作系统下是4byte 32bit,64位操作系统下是8byte 64bit。
- 二是这个占有的空间中存放一个地址,这个地址代表另一个空间,这个空间所占的大小信息由指针的类型表示。
- 指针赋值
- 直接
=
修改指针空间中保存的地址,从而改变指针的指向 - 解引用
*
之后再=
,是修改指针指向的空间所存放的值。
- 直接
具体
获得虚函数地址
根据操作系统的位数定义指针的大小:
#ifdef _xxx32 //代表是32位操作系统
typedef unsigned int POINTER_SIZE
#else
typedef unsigned long long POINTER_SIZE
#endif
我需要获得虚表。虚表被多态对象的虚指针所指向,虚指针在多态对象的最前面。
PolymorphicObject* ppO;
POINTER_SIZE* vtable = (POINTER_SIZE*)(*(POINTER_SIZE*)ppO);
每一次类型转换,实际上都会产生一个临时变量,上面复杂的式子可以拆分成:
POINTER_SIZE* temp1 = ppO;
POINTER_SIZE temp2 = *temp1;
POINTER_SIZE* temp3 = temp2;
POINTER_SIZE* vtable = temp3;
前面提到,指针指向空间的大小是由指针的类型所知道的,这里做的是
- 先将对象指针转换成
POINTER_SIZE*
,让编译器知道我们该空间的大小为sizeof(POINTER_SIZE)
(实际上ppO所指向的空间并不止这么大,但我们只要指针大小的信息)。 - 再将该指针所存储的地址取出来得到
temp2
,得到了虚表所在空间的首地址。 - 因为虚表本是一个(函数)指针数组,最后将地址再次解释成
POINTER_SIZE*
得到temp3,最终vtable
变量存储了虚表的首地址。
赋值给指针
我们知道函数的声明typedef void (__stdcall * pFunc)(void)
,要找的函数指针在虚表的INDEX
位置,从而vtable[INDEX]
就是目标函数指针的地址。
可此时得到的是POINTER_SIZE
类型,还需要赋值给函数指针从而进一步操作。看起来好像很复杂,实际上,根据指针赋值的第一条,直接:pFunc p = (pFunc)vtable[INDEX]
就搞定了。
PS:写完了才发现这么简单,当时是怎么想的,可能是看到函数指针就有点陌生,指针都只是无符号数而已,都是一样的-_-…