asm, c, c++ are my all
-- Core In Computer
posts - 139,  comments - 123,  trackbacks - 0
用内联汇编调虚函数, 理解VTABLE原理

虚函数和动态绑定是C++面向对象编程的核心内容之一。要理解C++虚函数的调用本质,就不得不说VPTR和VTABLE。所有拥有虚函数的C++类的大小都比可看到的内容多至少4个字节(如果派生树中存在多继承,就可能多于4个字节),这多出来的4个字节就是VPTR,它位于每个实例的最前方。VPTR的内容就是一个unsigned int的地址,指向一个内存区域,而这个被指向的内存区域就是VTABLE,所谓虚函数地址表。每个拥有虚函数的类都拥有一张VTABLE,里面是一个函数指针数组,每4个字节为一个单位,指向虚函数的入口地址。C++在调用虚函数时,首先要通过这个类的实例内容看到VPTR,从而找到VTABLE,然后根据要调用的虚函数,取相应偏移地址的内容,从而把调用转到这个位置。

以下是我用VC内联汇编编写的一个手工模拟此过程的示例,通过例子中的代码可以清楚的看出虚函数的调用过程。首先定义一个类,它有一个虚函数:


class  Class1
{
 
int
 i;
public
:
 Class1(
int  i)  this -> =  i; }

 
virtual   void  Print( int  a,  int  b)  { printf( " i=%d a=%d b=%d " , i, a, b); }
}
;

如果取这个类的大小,可以看到结果是8而不是4。
下面声明这个类的一个实例,并取得其VTABLE中第一个元素的值:

 Class1 *  pC  =   new  Class1( 1 );
 
int  addr  =   * ( int * )( * ( int * )pC);

注意取值的这一行运用了复杂的强制类型转换。我把它拆开解释一下。首先是取得对象pC的前四个字节的内容,只要把pC转换成int*然后直接取值就行了:
 *(int*)pC

下一步是把取得的这个值当作是一个指针,也就是再进行一次强制类型转换:
 (int*)(*(int*)pC)

最后取这个指针所指内存的内容,也就是VTABLE中第一个函数的地址了:
 *(int*)(*(int*)pC)

取得这个地址以后,下面就用汇编代码来调用这个地址所指的函数:

 __asm
 
{
  mov ecx, pC;
  push 
3
;
  push 
2
;
  call addr;
 }


注意调用类的非静态成员函数时需要先把对应实例的地址放到ECX寄存器中,也就是平常所说的“隐藏参数”了,然后为函数Print压两个参数进栈,根据运行的结果可以明显看出来调用类成员函数时也是从右向左压栈的,最后用call语句调用函数。不难发现调用类成员函数在参数个数确定时也是由被调用者负责弹栈,看来类成员函数也是可以声明为参数个数可变的函数了。

最后,执行程序,得到结果:
i=1 a=2 b=3 

posted on 2006-11-19 04:13 Jerry Cat 阅读(2776) 评论(4)  编辑 收藏 引用

FeedBack:
# re: 用内联汇编调虚函数, 理解VTABLE原理
2006-11-19 08:16 | Aki
"不难发现调用类成员函数时也是由被调用者负责弹栈,看来类成员函数是不能声明为参数个数可变的函数了"

可变个数参数函数跟压栈有关系还是弹栈有关系?  回复  更多评论
  
# re: 用内联汇编调虚函数, 理解VTABLE原理
2006-11-19 08:48 | Aki
再请教一个问题:如果this -> i = i;不加this-> 结果编译器也没有给加:
mov ecx, DWORD PTR _i$[ebp]
mov DWORD PTR _i$[ebp], ecx
这个是编译器的规定么,不能自动给加上么:
mov ecx, DWORD PTR _this$[ebp]
mov edx, DWORD PTR _i$[ebp]
mov DWORD PTR [ecx+4], edx
多谢!!  回复  更多评论
  
# re: 用内联汇编调虚函数, 理解VTABLE原理
2006-11-21 01:19 | Jerry Cat
已改 - 类成员函数是可以声明为参数个数可变的函数。
thiscall是C++成员函数调用的缺省约定.
(1). 参数从右向左入栈
(2). 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。
(3). 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈  回复  更多评论
  
# re: 用内联汇编调虚函数, 理解VTABLE原理
2007-11-13 16:59 |
好  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理



<2006年11月>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

常用链接

留言簿(7)

随笔档案

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜