通俗的说,栈帧就是C语言函数在调用的过程中的调用原理,就是当我们执行一个函数操作的时候,它的内部是如何实现的。栈帧不属于我们学习c的范围,但是栈帧对我们了解c有很大的帮助,所以让我们一起来学习学习栈帧吧!
一、 准备工作
1.在我们的c程序中第一个运行的不是main()函数而是mainCRTStartup()函数,main()函数是被mainCRTStartup()函数调用的。我们在VC6.0的环境下调试:查看—调试窗口—call stack(查看堆栈),如下图所示:
2.了解基本的地址空间。
3.常见的几种寄存器:
1)eax、ebx、ecx等存放变量;
2)ebp表示栈底寄存器;
3)exp表示栈顶寄存器;
4)eip(pc指针):程序计数器,保存当前正在执行指令的下一条指令地址。
4.了解CPU的基本用法:
5.了解常见的汇编指令:
1)mov:将后面的数据移动到前面的地址空间中
2)push:出栈
3)pop:入栈
6.下文中出现的的图都是个个过程操作完后的图。
二、栈帧调用原理
我们以下面一段代码为例,来演示栈帧调用的原理
#include<stdio.h>
#include<windows.h>
int myadd(int x,int y)
{
int z=x+y;
return z;
}
int main()
{
int a=0xaaaaaaaa;
int b=0xbbbbbbbb;
int c=myadd(a,b);
printf(" you should run here!\n");
printf("c:%d\n",c);
system("pause");
return 0;
}
1.栈帧结构
函数的栈帧:esp、ebp所表示的内存空间。如下图:
2.main()函数初化两个变量
![]()
mov:将后面的数据移动到前面的寄存器中
3.调用myadd函数之前的准备工作
先定义临时变量
a、b两个临时变量在main函数栈帧的底部形成
push:入栈
图中定义临时变量的汇编代码意思是:
将ebp-8移动到eax,再将eax入栈;
将ebp-4移动到ecx,再将ecx入栈。
注意:ebp- 就是地址向下走几个字节,ebp+ 就是地址向上走几个字节。
![]()
跳转至myadd函数
call命令:1)将当前call命令下的下条命令地址保存起来,为了恢复(调用完myadd函数后再回到main函 数);2)通过jmp修改eip的值,达到跳转的目的。将其入栈实现保存。
4.开辟myadd函数的栈帧
在这里,我们只关心前三行汇编代码
第一行汇编代码:将main函数的ebp入栈;
第二行汇编代码:将esp的内容赋值到ebp,即ebp和esp指向同一个位置;
第三行汇编代码:sub命令:前者减后者,即esp减44h,往低地址走;
因此形成了myadd函数的栈帧。
![]()
注意:每一个函数都有自己的一个栈帧。
5.调用myadd函数
计算a+b的值
第一行汇编代码:将[ebp+8](即a)放到eax;
第二行汇编代码:add命令:前者加后者,即 eax加[ebp+0Ch](即b);
第三行汇编代码:将eax移到[ebp-4]位置,定义z变量;
注意:[ebp-4]不改变ebp的值。
![]()
返回a+b的值
第一行汇编代码:将z的值存放到eax寄存器中
第二行汇编代码:将栈顶元素出栈到ebp,此时ebp指向main函数的栈底
第三行汇编代码: ret命令(返回命令):将当前栈顶的数据直接弹出至eip,从而达到返回目的。即返回至main函数。
6.返回到main函数
第一行汇编代码:add命令,前者加后者,即给esp+8地指向上走,相当于释放临时变量a、b
第二行汇编代码:定义一个c变量,将eax的值赋给它
![]()
以上就是栈帧调用原理的基本过程,第一次附图写博客,调理不清晰,请各路大神多多指点!!