美文网首页
栈中函数调用原理详解

栈中函数调用原理详解

作者: 细雨听风 | 来源:发表于2016-01-17 22:43 被阅读814次

    一、eip、ebp、esp介绍

        EIP,EBP,ESP都是系统的寄存器,里面存储的是些地址,我们系统中栈的实现上离不开他们三个。 我知道栈的数据结构主要特点是 后进先处。它还有两个作用: 1.栈是用来存储临时变量,函数传递的中间结果。 2.操作系统维护的,对于程序员是透明的。

        下面我们就通过一个小例子说说栈的原理。

         先写个小程序:

    void fun(void)

    {

        printf("helloworld");

    }

    void main(void)

    {

        fun()

         printf("函数调用结束");

    }

    当程序进行函数调用的时候,我们经常说的是先将函数压栈,当函数调用结束后,再出栈。这一切的工作都是系统帮我们自动完成的。但在完成的过程中,系统会用到下面三种寄存器:EIP、ESP、EBP。

    当调用fun函数开始时,三者的作用。

    EIP寄存器里存储的是CPU下次要执行的指令的地址。 也就是调用完fun函数后,让CPU知道应该执行main函数中的printf("函数调用结束")语句了。

    EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)

    ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶。并且始终指向栈顶。

    当调用fun函数结束后,三者的作用:

    系统根据EIP寄存器里存储的地址,CPU就能够知道函数调用完,下一步应该做什么,也就是应该执行main函数中的printf(“函数调用结束”)。

    EBP寄存器存储的是栈底地址,而这个地址是由ESP在函数调用前传递给EBP的。等到调用结束,EBP会把其地址再次传回给ESP。所以ESP又一次指向了函数调用结束后,栈顶的地址。

    二、堆和栈

    首先要清楚的是程序对内存的使用分为以下几个区:

    栈区(stack):由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似于数据结构中的栈。

    堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表。

    全局区(static):全局变量和静态变量存放在此。

    文字常量区:常量字符串放在此,程序结束后由系统释放。

    程序代码区:存放函数体的二进制代码。

    典型的内存区域分配如图所示:

    其次是堆和栈的申请方式:栈由系统自动分配,速度较快,在windows下栈是向低地址扩展的数据结构,是一块连续的内存区域,大小是2MB。堆需要程序员自己申请,并指明大小,速度比较慢。在C中用malloc,C++中用new。另外,堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。因此堆空间获取和使用比较灵活,可用空间较大。

    三、栈帧结构和函数调用过程

    首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。下图为典型的存取器安排,观察栈在其中的位置

    入栈操作:push eax; 等价于esp=esp-4,eax->[esp];如下图

    相关文章

      网友评论

          本文标题:栈中函数调用原理详解

          本文链接:https://www.haomeiwen.com/subject/lsrzhttx.html