一、什么是栈以及特点?
由编译器自动分配和释放,操作方式类似于数据结构中的栈。栈用于维护函数调用的上下文,离开了栈函数调用就没法实现。 “先进后出”
在计算机系统中,栈被定义为一个特殊的容器,用户可以将数据压入栈中,也可以将已经压栈的数据出栈;栈总是从高地址指向低地址,所以压栈操作使得栈增大,出栈使得栈减小;栈顶由称esp的寄存器进行定位,栈底由称为ebp的寄存器进行定位,压栈操作使得栈顶的地址减小,出栈的操使栈顶地址增大;
二、栈的作用?
栈保存了一个函数调用所需要的维护信息,这常常被称为堆栈帧;堆栈帧的一般包括:
- 函数的返回地址和参数;
- 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量;
- 保存的上下文:包括在函数调用前后需要保持不变的寄存器;
三、函数具体的堆栈调用
Main(调用方)函数中:
入栈:
- 压入调用main函数的函数maincrtstartup ()函数的栈底指针;
- 开辟main函数的栈帧空间;
- 压入形参以及调用函数;
- 压入回退到调用方时,被调用方的下一条指令的地址;
清栈:
- 清理形参变量地址;
- Sum(被调用方)函数中:
入栈:
- 压入调用sum函数的main函数的栈底指针;
- 开辟sum函数栈帧空间并且初始化0xCCCCCCCC;
- 压入实参;
退栈:
- 清理sum函数的栈帧;
- 回退栈底指针;
- 返回调用方栈帧后下一条指令;
下面是完整的代码演示:
#include<stdio.h>
int sum(int a,int b)
{
int tmp = 0;
tmp = a+b;
return tmp;
}
int main ()
{
int a =10;
int b =20;
int ret = 0;
ret = sum(a,b);
printf("ret = %d\n",ret);
}
正在上传…重新上传取消
转存失败重新上传取消
转存失败重新上传取消
正在上传…重新上传取消
转存失败重新上传取消
总结:
1、 压入实参 自左向右
2、 压下一行指令地址
3、 压调用方函数的栈底指针
4、 跳转到被调用方的栈帧
5、 开辟被调用方所需的栈空间
四、调用约定
- _cdecl C标准调用约定 (_cdecl 调用方开辟 调用方清理)
- _stdcall windows标准调用约定(_stdcall 调用方开辟 被调用方清理)
- _fastcall 快速调用约定(fastcall 前两个实参调用不开辟形参内存,第三个参数起)
- _thiscall 类成员方法调用约定
1、 函数的符号生成
2、 实参的入栈顺序
3、 形参的开辟和清理
相关面经:
1.返回值如何带出?
①如果 【函数的返回值<=4个字节】,则返回值通过寄存器eax带回。
②如果 【4<函数的返回值<=8个字节】,则返回值通过两个寄存器eax和edx带回。
③如果 【函数的返回值>8个字节】,则返回值通过产生的临时量带回。
2.形参开辟内存吗?由谁开辟?
形参开辟内存,由调用方开辟
3.形参的入栈顺序?
形参的入栈顺序为从右至左
4.被调用方结束后如何知道回退到调用方栈帧上?
通过将调用方函数的栈底的地址压入被调用方函数的栈底实现被调用函数结束后回退到调用方函数栈帧上。
5.函数调用完成如何知道执行下一行指令?
在将调用方函数栈底的地址压入被调用方函数栈底前先将调用方的call指令下一行指令的地址压栈实现函数调用完成后知道执行下一行指令的地址。