文章目录
前言
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
🎉推荐up主专题文章【C语言编程一百题】
📌QQ:3052645092 有问题可以一起讨论哦
🔎超级好用的刷题网站【牛客网】
🍭作者水平很有限,如果发现错误,请及时告知作者哦!感谢感谢!
栈帧
#include<stdio.h>
int MyAdd(int a, int b){
int c = 0;
c = a + b;
return c;
}
int main(){
int x = 0xA;
int y = 0xB;
int z = 0;
z = MyAdd(x, y);
printf("%d\n", z);
return 0;
}
我们用这个代码做测试
相关的寄存器
eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
ebp:栈底寄存器
esp:栈顶寄存器
eip:指令寄存器,保存当前指令的下一条指令的地址
相关指令
mov:数据转移指令
push:数据入栈,同时esp栈顶寄存器也要发生改变
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
sub:减法命令
add:加法命令
call:函数调用,1. 压入返回地址 2. 转入目标函数
jump:通过修改eip,转入目标函数,进行调用
ret:恢复返回地址,压入eip,类似pop eip命令
开始!!!
main函数调用MyAdd函数如何形成栈帧的我们就是讲解这个,这个明白啦,函数调用形成栈帧就明白啦
反汇编
主函数也是函数也会形成栈帧
x的初始化
y的初始化
z初始化
形成临时拷贝
y的值被压入栈
x的值被压入栈
总结:1,临时变量在函数被调用之前就已经形成了,临时拷贝 2,形参实例化的顺序是从右向左的!!!
接下来我们要调用函数了!!
函数被调用,其返回地址要被压栈保护
执行之前
执行之后
jump之前, jump修改eip的值,进入目标函数!!!
jump之后
进入MyAdd函数内部
现在我们正式进入了MyAdd函数
形成MyAdd函数栈帧指令1
ebp寄存器的内容压入栈!!!
未入栈之前
入栈之后
入栈成功
指令2
完成
执行之前
执行之后
到这可能有疑问,哪ebp之前存的是main函数栈底地址,那么esp的内容又把ebp的内容覆盖了,哪还能找回来吗?
答案是不用担心,我们已经push入栈了!!!
指令3形成栈帧的指令
形成前
形成后
现在我们的My Add函数的栈帧形成了 以下的一些代码我们关心,是一些对内存初始化的
我们直接来看MyAdd函数里面的临时变量的形成
此指令就是,把ebp-8所指向的这个内存单元赋上0
c=a+b的执行
以上三条指令的执行
汇编层面
第一条指令执行之前
执行之后
第二条执行之前
执行之后
第三条执行之前
执行之后
**c=a+b;执行完成
栈帧的形成我们已经完成了,还有一个细节要提一下
形成栈帧就是esp-某个值之后形成新的栈顶,和栈底地址形成了一块内存空间,但是问题是,编译器为啥能知道,要减多少呢,要形成多大的内存空间的!!!
解释:编译器是有能力知道的,总所周知,C语言是有类型关键字的,比如:int double float等,还有一个关键字叫左sizeof可以求各个类型数据的大小,那么sizeof求大小,是在编译时求还是在运行之后求出来呢?
答案是在编译时,因为sizeof是关键字。不是函数!!!因为这一点,编译器就有能力,判断出栈帧该申请多大空间
返回
本代码意思就是:把ebp-8内存空间的值,即c的值放到寄存器eax中 函数的返回值是通过寄存器的方式返回的
这三条我们不关心 主要看着3条
第一条指令
执行之前
执行之后
执行之前
执行之后
第二条指令
pop是弹栈指令,把栈顶的内容弹入ebp寄存器
执行之前
执行之后
第三条指令
ret返回地址压入eip,类似于pop eip
执行ret之后
安全返回,现在我们已经返回到了main函数
看这一条指令,x和y的释放
add esp, 8 意思是esp+8
最后一步啦
最后把eax寄存器放到z的空间
至此栈帧的调用和释放及返回操作完毕
根据临时拷贝的地址改变另一个临时拷贝
#include<stdio.h>
int MyAdd(int a, int b){
printf("before: %d\n",b);
*(&a + 1) = 100;//整型指针+1,本质是加上其所指向类型的大小
printf("after: %d\n", b);
return 0;
}
int main(){
int x = 0xA;
int y = 0xB;
int z = 0;
z = MyAdd(x, y);
printf("%d\n", z);
return 0;
}
通过形参a的地址来改形参b的值
总结
- 调用函数,需要先形成临时拷贝,形成过程是从右向左的
- 临时空间的开辟,是在对应函数栈帧内部开辟的
- 函数调用完毕,栈帧结构被释放掉
- 临时变量具有临时性的本质:栈帧具有临时性
- 调用函数是有成本的,成本体现在时间和空间上,本质是形成和释放栈帧有成本
- 函数调用,因拷贝所形成的临时变量,变量和变量之间的位置关系是有规律的
7.每个函数形成栈帧的指令都是一样,就是以下三条