C对象布局及多态实现之成员函数的调用

C对象布局及多态实现之成员函数的调用,第1张

C对象布局及多态实现之成员函数的调用,第2张

从这一部分开始,我们不仅要利用内存中打印的信息进行探究,还要跟踪观察编译器生成的汇编代码,了解编译器是如何实现这些语言特性的。汇编知识的讨论超出了本文的范围。我只分析与我们讨论相关的汇编代码。要理解本文要讨论的知识,不需要有完整的汇编知识,但必须知道基本概念。

让我们来看看引入虚拟继承的影响。为了比较,我们先来看看普通成员函数的调用。

执行下面的代码,包括对象的普通成员函数调用、类的静态成员函数调用和通过指针的普通成员函数调用:

C010 obj
PRINT _ OBJ _ ADR(obj)
obj . foo();
C012::sfoo();
C010 * pt = & obj;
pt-> foo();

结果如下:

obj的地址是:0012F843

这是obj对象的内存地址。

我们先看对象的普通成员函数调用,obj . foo();,对应的汇编代码是:

00422E09 lea ecx,[ebp fffff 967h]
00422 e0f call 0041 e289

第1行存储ecx寄存器中对象的地址。执行完这行指令后,我们会看到ecx中的值是0x0012F843,这是前面打印出来的值。如果函数需要传递参数,我们也会在前面看到一些推送指令。在第2行,我们可以看到调用是一个直接地址,这是静态绑定。也就是说,函数的调用地址是由编译器在编译时决定的。

当我们进去的时候,我们需要看到它是一个跳转指令。如果继续执行,可以看到真正的函数代码部分,如下(注:为了讨论方便,我在行前加了行号):

01 00425FE0推送ebp
02 00425FE1 mov ebp,esp
03 00425FE3 sub esp,0CCh
04 00425FE9推送ebx
05 00425FEA推送esi
06 00425FEB推送edi
07 00425FEC推送ecx
008

让我们看看第7行,将ecx寄存器放入堆栈。最后四行初始化函数堆栈中保存局部变量的部分。ecx值在第12行弹出。当您到达这里时,ecx值被保存为调用函数之前存储的对象的内存地址。第13行是将这个指针的值保存为一个局部变量。这样我们就知道,VC7.1不是像普通函数那样通过压栈来传递这个指针,而是通过ecx寄存器。第14行和第15行使用this指针为对象的成员变量赋值。

再看静态成员函数调用的汇编代码:

00422E14拨打0041DD84

很直接,因为它不需要处理这个指针,而且可以看出它也不需要处理这个指针,追溯到函数的汇编代码之后。这里没有列出具体的代码。

看看通过指针调用普通成员函数pt->foo()。,生成的汇编代码如下:

00422E25 mov ecx,dword ptr[ebp fffff 958h]
00422 e2b电话0041E289

类似于通过对象调用普通成员函数的代码。然而,将对象地址存储到ecx寄存器中是通过解引用pt指针来找到对象地址。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » C对象布局及多态实现之成员函数的调用

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情