说明
- C/C++软件运行时,内存根据使用方式的不同分为堆内存和栈内存,栈内存使用有以下特征:
- 栈内存使用(申请、释放)由系统自动分配和释放,程序员不用做任何操作。
- 栈内存重复使用,进入函数时数据入栈,函数执行完数据出栈。
- 函数中的局部变量以及实参保存在栈内存中。
内存模型
- 栈内存处于进程虚拟内存的高地址,从高往低扩展。
- 堆内存处于进程虚拟内存的低地址,从低往高扩展。
原理
- 在数据结构领域,栈是一种仅在尾部进行插入或删除操作的线性表,以后进先出的规则管理数据,先进入的数据被压入栈底,最后的数据在栈顶,压入数据叫做入栈,弹出数据叫做出栈。
- 函数调用关系也是一种栈的形式,先进后出,第一个函数(main)最后执行完,末端函数最先执行完。
- 因此使用栈的形式来管理函数调用中的临时内存使用(局部变量等)是非常合适的,而非临时的内存使用则需要通过另外一种方式(堆内存)来管理,所以系统将进程的虚拟内存划分为栈内存和堆内存。
- 栈内存和堆内存本质上就是一块内存空间,只是不同的使用方式而已,对于编译器就是不同的使用规范。
操作单元
- 入栈,出栈的操作单元并不是单个变量,而是整个函数,整个函数所需栈内存大小在编译时就能确认,入栈和出栈都是以整个函数需要的栈内存大小为单位一次性扩大/缩小栈内存空间。
- 以及其它使用考量,例如:函数调用栈回溯等,操作单元不仅仅是函数临时变量(局部变量,实参等)所需的栈内存空间,编译器还会保存一些其它信息,例如:为了解决函数调用栈中间函数跳转问题,需要将LR保存在栈中。
- 整个操作单元就叫做栈帧。
栈帧(Stack Frame)
- 每一次函数调用,都会在栈内存上维护一个独立的栈帧(stack frame),栈帧大小是编译时确认的,就是该函数需要占多少栈空间.每个独立的栈帧一般包括:
- 函数参数
- 临时变量: 包括函数的非静态局部变量以及编译器自动生成的其他临时变量
- 函数调用的上下文,例如:帧指针(Frame Pointer)和返回地址(LR)等
入栈、出栈操作
- 不同平台,函数调用的入栈和函数结束的出栈操作有细微差别,ARMV8平台操作。