堆栈基础

一、堆栈基础——内存区域

代码是以进程的形式来执行的,一个进程可能被分配到不同的内存区域去执行:

  • 代码区:这个区域内存储着被装入执行的二进制机器代码,处理器会到这个区域取指并执行
  • 数据区:用于存储全局变量/静态变量等
  • 堆区:进程可以在堆区动态的请求一定大小的内存,并在使用完之后还给堆区。动态分配和回收是堆区的特点,是向高地址扩展的内存区域,通常由程序员来管理
  • 栈区:用于动态的存储函数之间的调用关系,以保证调用函数在返回时恢复到母函数中继续执行,是由操作系统来管理的。


    image.png

栈区

栈是向低地址扩展的数据结构,入栈时是从高地址向低地址扩展,是一块连续的内存的区域。通常用来存储局部变量。栈顶的地址和栈的最大容量是系统预先规定好的,在windows下,栈的默认大小是2M,如果申请的空间超过栈的剩余空间,将提示overflow


image.png

堆区

堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。
操作系统中有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表

  • 寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序
  • 由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。
  • 对于大多数系统,会在这块内存空间中的首地址记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间


    image.png

区别

申请方式

栈:由系统自动分配。
堆:需要程序员自己申请,并指明大小,在c中malloc函数 例如p1=(char *)malloc(10)

申请效率

栈:系统自动分配,速度比较快,但是程序员无法控制
堆:由程序员分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来方便


二、堆栈基础——函数调用

函数调用时,将借助系统栈来完成函数状态的保存和恢复


image.png

主函数中调用函数A,函数A又调用的函数B,函数B的返回值加上一个值作为函数A的返回值,最后函数A的返回结果作为主函数的返回值


image.png
  • 这些代码区中精确的跳转都是在与系统栈巧妙的配合过程中完成的。
  • 当函数被调用时,系统栈会为这个函数开辟一个新的栈帧,并把它压入栈中。每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等待。
  • 当函数返回时,系统栈会自动弹出该函数所对应的的栈帧,弹出栈帧意味着这个函数执行完毕。


    image.png

    image.png

函数调用的步骤

栈是先进后出的数据结构

  • (1)参数入栈——将参数从右向左依次压入系统栈中
  • (2)返回地址入栈——将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
  • (3)代码区跳转——处理器从当前代码区跳转到被调用函数的入口(就是跳转到被调用的函数处,再取指令时就从被调用的函数的代码区开始取指令)
  • (4)栈帧调整。取指令时,会包括栈帧调整。具体包括
    保存当前栈帧状态值,已备后面恢复本栈帧时使用。
    将当前栈帧切换到新栈帧。

三、堆栈基础——常见寄存器和栈帧

寄存器

寄存器是中央处理器CPU的组成部分。寄存器是有限存储容量的高速存储部件,它们可以用来暂存指令、数据和地址。我们常常看到32位CPU、64位CPU这样的名称,其实指的就是寄存器的大小。32位CPU寄存器大小就是4字节。CPU越大说明CPU一次处理的数据就越多
为了缓解CPU和内存效率不对等的问题,CPU自带一级缓存和二级缓存,但是这些缓存还是不够快,于是将一些最常访问的数据存放在寄存器中(寄存器的读写速度比内存快很多),CPU优先读取寄存器,再由寄存器跟内存交换数据。

image.png

CPU、寄存器和内存
CPU运算所需数据只能从寄存器存取,寄存器会先从高速缓存区读取数据,不命中才会从内存读数据。而内存中的数据也是先从磁盘缓存区(disk cache)读数据,或者在磁盘缓冲区(disk buffer)存数据,最后才会交际最慢的磁盘。

ESP和EBP两个寄存器

image.png
栈指针寄存器ESP(extended 寄存器 stack栈 pointer指针)

ESP标识了当前栈帧的顶部(相当于top)

基址指针寄存器ESP(extended 寄存器 base基址 pointer指针)

ESP标识了当前栈的底部(相当于base)

ESP和EBP之间的内存空间为当前帧,当前帧指的是系统栈中最顶部的栈帧。**


image.png

函数栈帧中包含的信息

  • 局部变量
  • 栈帧状态值,用于在本帧被弹出后恢复出上一个栈帧
  • 函数返回地址,以便函数返回时能够恢复到函数被调用前的代码区中继续执行指令


    image.png

指针寄存器EIP(extended 寄存器 instruction pointer指针)

指针寄存器,期内存放着一个指针,该指针永远指向下一条等待执行的指令地址。
函数调用完毕后,返回原来函数指令运行的一个关键操作是,将栈帧中保持的返回地址装入EIP寄存器。
控制了EIP寄存器就相当于控制了进程,我们让EIP指向哪里,CPU就会去执行哪里的指令。


image.png

image.png

之所以将ESP值装入EBP是因为新的栈帧是位于旧的栈帧上面,所让旧的栈帧的顶部ESP成为新的栈帧的底部EBP

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。