函数堆栈(函数栈帧)

标签: 函数栈帧
53人阅读 评论(0) 收藏 举报

函数的调用过程

每一次函数的调用都是一个过程,这个过程我们称之为函数的调用过程。这个过程需要为函数开辟栈空间,用于函数调用中临时变量的保存,保护。这块栈空间我们称之为函数栈帧

先写一段代码:

#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 = 0;
    ret = Add(a,b);
    printf("ret = %d\n",ret);
    return 0;
}

在调用main函数之前

  • 先按F10进行逐过程调试
    这里写图片描述
    可以看到在调用主函数main之前,还调用了
    __tmainCRTStartup()函数 和 mainCRTStartup()函数。
  • 然后我们对准主函数main右键,转到反汇编。
    这里写图片描述
    可以观察到先是移动ebp、esp两个寄存器。
    ebp-保存堆栈底部的地址
    esp-保存堆栈顶部的地址
    在调用main函数之前,__tmainCRTStartup()函数的堆栈
    这里写图片描述
  • 在反汇编里继续往下走
    堆栈从下往上是由高地址到低地址,函数一步步往后走,堆栈是逐步往上面压栈。
    这里写图片描述
    这里写图片描述
    注:每个方块都为4个字节
    1、在__tmainCRTStartup()上压一个栈,移动esp,ebp。
    2、为main函数预开辟一个E4h大小的空间。
    3、在预开辟空间上压三个栈ebx,esi,edi(先不做介绍,因为到最后这三个栈会被原封弹出栈空间)。
    4、将为main开辟的空间初始化为CCCCCCCC。

开始执行main函数

  • 定义变量赋值
    这里写图片描述
    1、创建a,放在[ebp-8h],从ebp开始向上数第8个字节的空间用来存放a的值0Ah。
    2、创建n,放在[ebp-14h],从ebp开始向上数第20个字节的空间来存放b的值14h。
    3,、创建ret,放在[ebp-20h],从ebp开始向上数第32个字节的空间来存在ret的值0。
  • 准备给函数传参
    这里写图片描述
    1、将[ebp-14h](也就是b)赋值给eax,并把eax压栈。
    2、将[ebp-8](也就是a)赋值给ecx,并且压栈。
    3、开始调用函数(记住call前面的地址),当F10走到这里时,按F11进入函数,然后继续F10。
    这里写图片描述
    此时,又会在栈顶部压一个地址,用来存放调用函数的地址(以便用完函数可以准确的跳回主函数main)
    这里写图片描述

函数的调用

  • 跟main函数一样,Add函数会在栈内开辟一部分空间
    这里写图片描述
    1、将main函数的ebp保存起来压栈(以便调用完Add函数后可以返回到main函数的底部)。
    2、将新的ebp放在栈顶(Add函数堆栈的底部)
    3、为Add函数预开辟一片空间大小为CCh字节
    4、在顶部继续压三个栈ebx,esi,edi。
    最后将为Add开辟的CCh字节大小的空间全部初始化为全C。
  • 开始执行函数体
    这里写图片描述
    1、定义z,[ebp-8]即ebp向上数第8个字节处空间为z=0。
    2、将[ebp+8]也就是ebp向下数第8个字节处,也就是a的值20,赋值给寄存器eax。
    3、将[ebp+0Ch]也就是ebp向下数第12个字节处,也就是b的值加到eax里,也就是将10和20加起来放到寄存器eax中。
    4、将eax赋值给[ebp-8],也就是z。
    5、将z的值赋值给寄存器eax(将z的值保存起来)。

开始销毁函数体

这里写图片描述
1、弹出edi,esi,ebx三个压栈。
2、将esp移动到ebp位置,此时Add函数直接全部弹出(调用完毕)
3、弹出ebp,因为当初保存的main函数的ebp,所以此时ebp返回到main底部。ret弹出刚才保存的调用函数的地址,main下面的栈空间。
Add函数完成销毁。
新的esp、ebp如下:
这里写图片描述

回到主函数main

这里写图片描述
1、esp加8,esp向下移8个字节。也就是将ecx,eax弹出。即a跟b的临时拷贝(形参)移出。
2、将Add函数内部保存的eax的值赋给[ebp-20],也就是将Add函数计算好的值返回给ret。
结果ret计算完成。
- 下面放一个整体的图
这里写图片描述
函数栈帧部分内容在不同编译器上出现的结果不同,但是思想都是一致的。小弟用vs2008献丑一把,望大神指导。

查看评论

王志东说嘴打嘴

几天来,王志东离开新浪几乎成了IT界最大的热门事件。想不到王志东的口碑是这样好,看来,这位遭受不幸的创业者拥有着最广泛的同情和怜惜。确实,曾被人根据体形戏称为“肥而不腻”的王志东,正是一位凭自己的不懈...
  • stanely
  • stanely
  • 2001-06-07 15:12:00
  • 655

函数栈帧图解

我们知道内存空间大致可以用下图表示: 而函数在调用的时候都是在栈空间上开辟一段空间以供函数使用,所以下面来详细谈一谈函数的栈帧结构。 如...
  • stay_the_course
  • stay_the_course
  • 2016-11-05 11:55:09
  • 2952

堆栈-栈帧-函数调用过程分析

  • 2011年11月24日 21:51
  • 51KB
  • 下载

C语言函数调用及栈帧结构

一、地址空间与物理内存 (1)地址空间与物理内存是两个完全不同的概念,真正的代码及数据都存在物理内存中。 物理储存器是指实际存在的具体储存器芯片,CPU在操纵物理储存器的时候都把他们当做内存来对待...
  • qq_29403077
  • qq_29403077
  • 2016-11-17 19:10:11
  • 3529

Windows x64 栈帧结构

Windows x64 栈帧结构 一、前言   Windows 64位下函数调用约定变为了快速调用约定,前4个参数采用rcx、rdx、r8、r9传递,多余的参数从右向左依次使用堆栈...
  • yy405145590
  • yy405145590
  • 2017-06-22 20:55:44
  • 465

函数调用栈帧过程带图详解

这里,我们来研究如下代码的栈帧过程。为了初学者理解汇编指令,所以编译环境是在vc++6.0下#include #includeint sum(int _a,int _b) { int c=0;...
  • IT_10
  • IT_10
  • 2016-10-31 22:29:29
  • 2225

堆栈、栈帧、函数调用过程

一、堆和栈首先,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。程序对内存的使用分为以下几个区:(1)栈区(stack):由编译器自动分配和...
  • will130
  • will130
  • 2015-10-26 14:11:35
  • 596

函数入栈出栈以及栈帧

参考一 函数调用是程序设计中的重要环节,也是程序员应聘时常被问及的,本文就函数调用的过程进行分析。 一、堆和栈 首先要清楚的是程序对内存的使用分为以下几个区: l ...
  • tkp2014
  • tkp2014
  • 2015-10-02 21:22:28
  • 1039

程序语言的底层描述(4)——递归函数汇编栈帧实现

这一节我们来讨论递归函数栈帧实现、
  • u013471946
  • u013471946
  • 2014-10-13 13:33:31
  • 1188

x86-64 下函数调用及栈帧原理

一蓑一笠一扁舟,一丈丝纶一寸钩。 一曲高歌一樽酒,一人独钓一江秋。 ——题秋江独钓图缘起在 C/C++ 程序中,函数调用是十分常见的操作。那么,这一操作的底层原理是...
  • lqt641
  • lqt641
  • 2017-06-10 19:06:32
  • 1174
    个人资料
    持之以恒
    等级:
    访问量: 316
    积分: 171
    排名: 102万+
    文章存档
    最新评论