中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
虚函数的内存结构(一)
作者:未知 时间:2005-09-13 23:28 出处:Blog.ChinaUnix.net 责编:chinaitpower
              摘要:虚函数的内存结构(一)
 这段日子面试了两次有关C++的工作,面试过程中都遇到了有关虚拟函数的问题。第一次遇到该问题的时候,我只能说出虚拟函数的用法,但具体在内存中的排布就不知道了。面试完之后,马上到书店找了一本《inside the c++ object model》,由于没有找到侯捷译的中文版,拿了本英文版的回去。英文较菜,也没有定下心来看,结果第一章的内容就理解错了。第二次面视的时候,还是同样的问题,结果回答错了。昨天晚上又重新看了一晚上,感觉对这部分内容有了正确的理解。决定把自己理解的内容,拿出来,争取能和大家分享一下,有错误的地方,也欢迎高手的斧正。

一,C语言中函数的调用方式

 

没有理解虚拟函数在内存中的处理形式,我觉得是自己在看书的时候缺乏思考造成的,只是跟着书本走,但没有仔细去想相关的内容。所以造成了思维闭路,导致很多东西理解错误。我觉得理解虚函数,还得从对函数在代码中的组织形式开始。好了,先谈谈我对函数的理解吧。

我们知道一个可执行有数据段,代码段等来存储文件的执行信息。而汇编就是用这种格式来写代码的,高级语言如C等都需要把文件转化为汇编之后,进行编译,最终生成可执行文件。所以把C文件转化为汇编,可能能更好的理解代码的组织形式。

我们先来看一段简单的C文件:

Void first()

{

}

Void main()

{

     frist();

}

 

main中是通过何种形式来调用first的呢?学过汇编的话,我们知道汇编   中的函数的调用是通过call指令来实现的。试着编译以上的C程序,并通过dumpbin反编译它的exe文件,我们可以得到以下一段代码。

 

  00401000: CC                 int         3

  00401001: CC                 int         3

  00401002: CC                 int         3

  00401003: CC                 int         3

  00401004: CC                 int         3

@ILT+0(_first):

  00401005: E9 16 00 00 00     jmp         _first

@ILT+5(_main):

  0040100A: E9 41 00 00 00     jmp         _main

  0040100F: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

  0040101F: CC                                               Ì

_first:

  00401020: 55                 push        ebp

  00401021: 8B EC              mov         ebp,esp

  00401023: 83 EC 40           sub         esp,40h

  00401026: 53                 push        ebx

  00401027: 56                 push        esi

  00401028: 57                 push        edi

  00401029: 8D 7D C0           lea         edi,[ebp-40h]

  0040102C: B9 10 00 00 00     mov         ecx,10h

  00401031: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  00401036: F3 AB              rep stos    dword ptr [edi]

  00401038: 5F                 pop         edi

  00401039: 5E                 pop         esi

  0040103A: 5B                 pop         ebx

  0040103B: 8B E5              mov         esp,ebp

  0040103D: 5D                 pop         ebp

  0040103E: C3                 ret

  0040103F: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

  0040104F: CC                                               Ì

_main:

  00401050: 55                 push        ebp

  00401051: 8B EC              mov         ebp,esp

  00401053: 83 EC 40           sub         esp,40h

  00401056: 53                 push        ebx

  00401057: 56                 push        esi

  00401058: 57                 push        edi

  00401059: 8D 7D C0           lea         edi,[ebp-40h]

  0040105C: B9 10 00 00 00     mov         ecx,10h

  00401061: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  00401066: F3 AB              rep stos    dword ptr [edi]

  00401068: E8 98 FF FF FF     call        @ILT+0(_first)

  0040106D: 5F                 pop         edi

  0040106E: 5E                 pop         esi

  0040106F: 5B                 pop         ebx

  00401070: 83 C4 40           add         esp,40h

  00401073: 3B EC              cmp         ebp,esp

  00401075: E8 16 00 00 00     call        __chkesp

  0040107A: 8B E5              mov         esp,ebp

  0040107C: 5D                 pop         ebp

  0040107D: C3                 ret

  0040107E: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

  0040108E: CC CC                                            ÌÌ

 

该段代码中有一些是调试信息,不需要管它。我们知道程序一定从_main来执行,调用开始会有一些参数信息去配置,而后就通过00401068: E8 98 FF FF FF     call        @ILT+0(_first) 跳转到first函数。这样应该对C生成的执行文件的代码段有一个基本的了解,所有的函数被放到了代码段中,各个函数的调用是通过call来转到函数的开始地址来执行(当然每个函数都需要有自己独特的名字,其实到了具体的执行文件中,是通过数字来定位这些信息的)。

 

二,C++中非虚函数的调用方式

 

先看如下一段代码:

 

class point

{

public:

            void first();

};

 

void point::first()

{

            return;

}

 

void main()

{

            point pt;

            pt.first();

}

 

反编译后程序如下:

00401000: CC                 int         3

  00401001: CC                 int         3

  00401002: CC                 int         3

  00401003: CC                 int         3

  00401004: CC                 int         3

@ILT+0(_main):

  00401005: E9 16 00 00 00     jmp         _main

@ILT+5(?first@point@@QAEXXZ):

  0040100A: E9 51 00 00 00     jmp         ?first@point@@QAEXXZ

  0040100F: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

  0040101F: CC                                               Ì

_main:

  00401020: 55                 push        ebp

  00401021: 8B EC              mov         ebp,esp

  00401023: 83 EC 44           sub         esp,44h

  00401026: 53                 push        ebx

  00401027: 56                 push        esi

  00401028: 57                 push        edi

  00401029: 8D 7D BC           lea         edi,[ebp-44h]

  0040102C: B9 11 00 00 00     mov         ecx,11h

  00401031: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  00401036: F3 AB              rep stos    dword ptr [edi]

  00401038: 8D 4D FC           lea         ecx,[ebp-4]

  0040103B: E8 CA FF FF FF     call        @ILT+5(?first@point@@QAEXXZ)

  00401040: 5F                 pop         edi

  00401041: 5E                 pop         esi

  00401042: 5B                 pop         ebx

  00401043: 83 C4 44           add         esp,44h

  00401046: 3B EC              cmp         ebp,esp

  00401048: E8 43 00 00 00     call        __chkesp

  0040104D: 8B E5              mov         esp,ebp

  0040104F: 5D                 pop         ebp

  00401050: C3                 ret

  00401051: CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC     ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ

?first@point@@QAEXXZ:

  00401060: 55                 push        ebp

  00401061: 8B EC              mov         ebp,esp

  00401063: 83 EC 44           sub         esp,44h

  00401066: 53                 push        ebx

  00401067: 56                 push        esi

  00401068: 57                 push        edi

  00401069: 51                 push        ecx

  0040106A: 8D 7D BC           lea         edi,[ebp-44h]

  0040106D: B9 11 00 00 00     mov         ecx,11h

  00401072: B8 CC CC CC CC     mov         eax,0CCCCCCCCh

  00401077: F3 AB              rep stos    dword ptr [edi]

  00401079: 59                 pop         ecx

  0040107A: 89 4D FC           mov         dword ptr [ebp-4],ecx

  0040107D: 5F                 pop         edi

  0040107E: 5E                 pop         esi

  0040107F: 5B                 pop         ebx

  00401080: 8B E5              mov         esp,ebp

  00401082: 5D                 pop         ebp

  00401083: C3                 ret

  00401084: CC CC CC CC CC CC CC CC CC CC CC CC              ÌÌÌÌÌÌÌÌÌÌÌÌ

 

 可以看到在main函数中还是通过0040103B: E8 CA FF FF FF     call        @ILT+5(?first@point@@QAEXXZ)来调用对象中的函数的,而C中的_frist变成了first@point@@QAEXXZ是因为不同的类中可能有函数名相同的函数,这样为了区别这些函数,就需要为这些函数添加一些特别的信息,如类的名称等信息。(还有就是为了区别函数重载)

         这样其实就可以知道在C++的类中,非虚函数其实和C中的函数一样被组织在代码段中,只是命名的方式并不一样。

 

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有