程序员的内功修炼——深入理解函数栈帧

Add函数汇编代码的解析
1. 写过无数代码的你是否想过这些问题?
1.局部变量是怎么创建的?
2.为什么局部变量的值是随机值?
3.函数是怎么传参的?传参的顺序是怎么样的?
4.形参和实参是什么样的关系?
5.函数调用是怎么做的?
6.函数调用之后结束是怎么返回的?
看到这些题目是否觉得自己并没有掌握函数的调用等等一系列的问题?不用着急  , 接下来我们慢慢探索 。那我们该怎么去了解呢?我们这次使用的编译环境是 。为什么使用呢?因为越高级的编译器越不容易观察到函数的栈帧 , 而且不同的编译器 , 函数调用过程中栈帧的创建也是略有差异的 。那接下来就进入正题:
2.了解寄存器和大概轮廓
我们的寄存器有许多  , 例如 eax , ebx , ecx , edx 。还有ebp , esp , 今天我们重点观察的就是ebp寄存器和esp寄存器 。
ebp寄存器和esp寄存器中存放的是地址 , 这两个地址是维护函数栈帧的 。edp是栈底的指针 , 即指向的是函数开辟空间的底部的 , esp是栈顶指针 , 即指向的是函数开辟空间的顶部的 。每一个函数调用 , 都有在栈区上创建一块空间 。今天我们研究的代码是:
【程序员的内功修炼——深入理解函数栈帧】#includeint 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;}
首先简单的了解 , 在我们调用函数是实在栈区上开辟空间 , 我们还需要知道栈区使用的规则是先用高地址再用低地址(即低地址是栈顶) , 如果低地址有数据等等是不能直接使用高地址的数据的 , 我们在低地址的地方放数据是压栈的过程 , 删除低地址的数据是出栈的过程 。如下图:
那你知道其实main函数也是被调用的函数吗?
将上面的代码在上进行调试(ctrl+F10)然后再点击调试---->窗口---->调用堆栈 。
然后再f10 知道代码结束就能看到这个:
我们就可以知道main函数其实也是被调用的函数 , 它也是被函数调用的 。
接下来我们还可以看到 , 函数又是被调用的 。
总结画图就是这样的:
所以在刚刚画图的过程当中我在main函数的下面还空了一部分空间 , 其实就是这些函数的空间 。
观察汇编代码
接下来就是真正进入了主题 , 根据汇编代码来分析函数栈帧到底是怎么一回事 。
我们开始调试(Ctrl+f10)然后右键鼠标 , 点击转到反汇编 。
1.分析main函数的汇编代码:
看到代码是不是有点慌张 , 不要慌张 , 我会一条一条的解释的 。
我们进入main函数之前函数的堆栈应该是这样的
第一条代码(push):
00553B40pushebp
第一条:push 用英文翻译就是压栈的意思 , 其实就是在栈顶上放一个元素 。
就是在栈顶上放一个ebp的寄存器的地址 。这个时候我们需要观察一下地址的变化 , 我们要记住的就是esp寄存器始终是指向栈顶元素的 , 不管是push(压栈) , 还是pop(出栈) , esp的地址都会发生改变的 。我们调试是时候打开窗口----->内存----->内存1(随便一个都可以) 。
首先观察esp最初的地址(操作如图所示)
然后打开监视: