栈帧结构的细化(C语言)

C语言中关于栈帧:由于在函数调用时往往会形成栈帧结构,为此我们经常有以下几个疑问:


1.只要给函数传递参数就会形成临时变量,这些临时变量会存在栈上,具体怎样存的?

2.函数内部定义的变量叫局部变量(自动变量),这些变量调用时创建,调用完成后自动释放,为什么?

3.函数调用完成之后应该返回到原来调用的地方,那之前要做什么?

4.函数返回时的临时变量存在哪里?


关于了解栈帧,我们需要知道一些预备知识:


1.寄存器:具有存储功能的离cpu最近的存储单元,速度最快

(1)ebp:基址寄存器(栈底)寄存器

(2)esp:栈顶寄存器

(3)pc指针(程序计数器):指向当前正在执行指令的下一条指令

(4)eax,ebx...通用寄存器

2.cpu的任务:(1)取指令(从内存取到cpu内部) (2)分析指令(根据指令集、操作数)  (3)执行指令

3.指令集:精简指令集、复杂指令集

4.任何一个函数都有属于自己的ebp(栈底)、esp(栈顶);但是内存中的ebp、esp只有一份;为了保证一个函数不会覆盖另一个函数的ebp、esp,调用时必须对原来函数

的ebp(栈底)、esp(栈顶)进行保存;因为ebp(栈底)、esp(栈顶)永远指向当前函数最新的栈底、栈顶;栈是从(地址减小)的方向生长

6.函数调用时,相关寄存器内容必须保存,目的是调用完之后恢复(程序切换也要保存,只是os的问题)

7.call指令的任务: 

(1)跳转到被调用函数的入口处(通过调整pc指针完成)

(2)保存当前正在执行指令的下一条指令的地址到栈中

8.push:把数据压到栈顶;pop把栈顶数据拉出来放在指定寄存器里

9.形参实例化时从右往左顺序进行的 

10.将数据压入栈顶时,先移动指针再放数据,保证栈顶指针永远指向一个有效的数据

11.调用函数时,先形成实参的临时变量,再执行call命令

12.第一个参数与函数返回地址相邻;

13.先将栈底地址保存,再将栈顶内容赋值给栈底,(栈底指向现在的栈顶)


为了讲解清楚栈帧,我们以下面一个小小的程序为例:


#include <stdio.h>
#include <windows.h>
int fun(int x,int y)
{
	int c=0xcccccccc;
}
int main()
{
	int a=0xaaaaaaaa;
	int b=0xbbbbbbbb;
	int ret=fun(a, b);
	printf("you should running here\n");
	system("pause");
	return 0;
}

具体执行过程,我们以指令在计算机内部过程来剖析:

开始,寄存器ebp、esp分别指向main函数的栈底、栈顶(main函数是入口),pc指针指向main函数的代码区;

(1)定义变量a,b;反汇编:dword ptr [a],0AAAAAAAAh ,dword ptr [b],0BBBBBBBBh 

(2)开始调用:但是调用之前要形参实例化形成临时变量:(注意形参从右往左进行实例化)

     mov         eax,dword ptr [b] :把变量b存到通用寄存器eax中 

     push        eax  :把eax中(b)压入堆栈中

     mov         ecx,dword ptr [a]  :把变量b存到通用寄存器ecx中

     push        ecx  :把ecx中(b)压入堆栈中

     使用call指令开始调用函数:call        @ILT+295(_fun) (0F9112Ch) ;注意call指令完成两个任务:(1)跳转到被调用函数的入口处(通过调整pc指针完成)

(3)保存当前正在执行指令的下一条指令的地址到栈中

    add         esp,8  :保存当前正在执行指令的下一条指令的地址到栈中

    mov         dword ptr [ret],eax  :跳转到被调用函数的入口处

(4)这时已经来到fun函数:但是现在系统的ebp、esp还指向原来main函数的栈底和栈顶,显然我们应该马上修改二者的值:

      push        ebp  :把ebp(main函数的栈底内容)中的内容压入栈顶

      mov         ebp,esp :把esp内容放到ebp中,所以上面先保存ebp内容;此时显示的栈底指向栈顶 

      sub         esp,0CCh :栈顶向下移动

  现在的栈底、栈顶所组成的部分为被调用函数的栈帧结构

(4)恢复栈帧结构:

     mov         esp,ebp  :把ebp里内容复原给esp,恢复栈底

     pop         ebp  :然后把栈顶内容返回给ebp,恢复栈顶

     ret :恢复pc指针的指向(指向main函数的函数返回地址)

     add         esp,8  :栈顶向上移动,释放实参实例化的临时变量


以上的执行过程用简单的图标描述如下:



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值