一、需要的知识储备(准备工作)
1、常用寄存器类型
<1>、 sp/esp/rsp 栈顶指针
<2>、 bp/ebp/rbp 栈底指针
这两个指针的作用是维护栈帧的范围
<3>、 eax 累加寄存器
<4>、 ebx 基地址寄存器,在内存寻地址时存放基地址
<5>、 ecx 计数寄存器
<6>、 edx 多功能寄存器,总是被用来存放整数除法产生的余数
2、部分汇编指令的认识
<1>、push指令:称为压栈,从栈顶增加一个元素。具体操作为:它首先减少esp的值,即esp向上移动,再将操作数复制到esp所指向的地址,在32位计算机下,esp每次减少4个字节
<2>、mov指令:用于将一个数据从源地址传送到目标地址,原操作地址的内容不变
注解:此处即为esp的值给ebp,就是ebp移动到esp的位置
<3>、pop 指令:称为出栈,和压栈相对应的从栈顶删除一个元素,同时esp++
<4>、call 指令:将该指令的下一条指令的地址压入栈中,并跳转(jump)到子函数中
<5>、sub 指令:减操作指令,从寄存器减去数值,然后将结果保存到寄存器中
<6>、add 指令:加操作指令,从寄存器加上数值,然后将结果保存到寄存器中
<7>、lea 指令:为“load effective address”的缩写,可以将地址赋给目标寄存器
<8>、rep 指令:重复指令
注解:即从edi开始39h(十六进制)个空间全部令为eax的内容,即0cccccccch,(相当 于初始化)每次操作四个字节(每个word是两个字节,dword为四个字节)
3、栈的存储方式以及main函数的栈帧
1>、栈的存储方式
在栈中,上位是低地址,下位是高地址,在函数调用时会优先分配高地址空间,然后再分配低地址空间。
2>、main()函数的栈帧
在C语言中main函数也是被调用的,如下图中:
<1>、代码先给mainCRTstartup分配空间,然后进入mainCRTstartup中调用__tnainCRTstartup
<2>、分配空间给__tnainCRTstartup,然后进入__tnainCRTstartup,再调用main
<3>、分配空间给main,然后才进入main函数中
从vs上演示:
注释:vs2019及以上版本无法演示调用main函数的函数
用vs2013如下图所示:
用F10调试,查看调用堆栈,跳出main函数后,发现main函数之外还有两个被调用的
在此处:即mainCRTstartup调用__tnainCRTstartup
进入mainCRTstartup函数后 __tnainCRTstartup调用main函数
二、函数栈帧的创建和销毁
代码如下(示例):
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\n", c);
return 0;
}
1、关于main()函数的操作
1>、为main()函数开辟栈帧
2>、在main()函数中创建变量
2、关于Add()函数操作
1>、调用Add()时的准备
** **
2>、给Add()函数开辟栈帧
3>、在Add()函数中创建变量并计算
4>、Add()栈帧的销毁
3、返回main()函数
总结
看到这里,想必之前存在的很多疑问已经迎刃而解。
比如:
1、局部变量是怎么创建的?
2、为什么局部变量的值是随机值?
3、函数是怎么传参的?传参顺序是怎样的?
4、形参和实参是什么关系?
5、函数调用是怎么做的?
6、函数调用是结束后怎么返回的?
版权归原作者 学Java的冬瓜 所有, 如有侵权,请联系我们删除。