为什么要有函数
函数就是一系列C语句的集合,为了完成某个会重复使用的特定功能。
需要该功能的时候,直接调用该函数即可,不用每次都堆叠一大堆的代码;
需要修改该功能的时候,也只要修改和维护这一个函数即可。
总之,将语句集合成函数,好处就是方便代码重用。并且,一个好的函数名,
可以让人一眼就知道这个函数实现的是什么功能,方便维护。
##函数的调用过程
那我们深入的研究一下函数的调用过程
先看看下面这段代码,当我们进行调试的时候,查看【调用堆栈】
#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 ret = Add(a, b);
printf("ret = %d\n", ret);
return 0;
}
- 查看调用堆栈,我们可以发现其实 main 函数在_tmainCRTStartup 函数中 调用的,
而_tmainCRTStartup函数在 mainCRTStartup 被调用的
- 每一次函数调用都是一个过程,通常称为:【函数的调用过程】
- 函数要跑起来的话,它要向内存申请一段空间;而这段空间在栈上开辟,那么这块
栈空间我们称之为【函数栈帧】;记住栈的使用顺序是由高地址到低地址。
- 我们平时知道的寄存器有eax,ebx,ecx,edx;
- 还有两个ebp和esp,在函数调用的过程中这两个寄存器存放了维护这个栈的
栈底和栈顶指针。来维护开辟出这段地址的起始和结尾的内存地址。
######调用main函数,我们要为main函数分配栈帧空间,栈帧维护如下:
######ebp指向了函数栈帧栈底的地址;esp指向了函数栈帧栈顶的地址。
当我们要详细的研究函数调用过程,必须得对应汇编代码。
从main函数开始,要展开main函数的调用就得为main函数创建栈帧。
那我们先来看main函数栈帧的创建;
接下来是Add函数的调用。参数传递过程:
执行call指令的时候按F11,来到了这里
再按F11就进入Add函数的执行代码处
剩下的是函数返回部分
栈帧这部分内容在不同的编译器上实现存在差异,但是思想是一致的