程序在内存中的存储分为三个区域,分别是动态数据区、静态数据区和代码区。函数存储在代码区,全局变量以及静态变量存储在静态数据区,而在程序执行的时候才会在动态数据区产生数据。程序执行的本质就是代码区的指令不断执行,驱使动态数据区和静态数据区产生数据变化。

代码区与动态数据区由三个寄存器控制,分别是eip、ebp和esp。eip指向代码区下一个要执行的指令,ebp与esp分别指向动态数据区的栈底和栈顶。初始情况下eip默认指向main函数的第一条指令,esp、ebp指向的位置由程序加载时内核的设置决定。

我们看一下这段代码如何执行的,在执行第一条指令时,考虑到函数调用的问题,ebp会先把当前指向的地址记录到栈中,方便以后返回来继续执行。把地址压进栈时,esp就会自动往栈顶方向移动。说到这里,为避免混淆先科普一下什么是栈顶和栈底,栈只允许在一端做插入和删除操作,这一端就叫栈顶,而另一端叫做栈底,图中下方叫栈顶,上方叫栈底。esp永远在栈顶,也就是图的最下方。

由于esp指向的地址已经被记录,那么它就被空闲出来了。现在我们开始构建main函数的栈,空闲的esp帮忙看着main函数的栈底。这个时候esp与ebp是重叠的。

eip继续指向下一条指令,到了局部变量i的初始化,这里将i赋值为4,就将i的初始值压到栈中,esp继续往栈顶移动。下一条指令与本条相同,将局部变量j也压入栈中,如图所示。

接下来调用了fun函数,虽然fun函数是独立的函数,但是由于是在main函数中调用的,所以依然将数据压至main函数的栈中。fun函数的传入参数为i、j,但是入栈的顺序正好相反,b先入栈,然后a被压入栈中,如图所示。

接下来要跳转到fun函数了,在跳转之前,我们要先给fun函数的返回值留个位置,因为要赋值给局部变量m的。然后再将fun函数的返回地址压入栈中,方便执行完fun函数后能继续往下执行。最后再把ebp当前的地址值压入栈中,此时ebp指向的是main函数的栈底(如果这里不做保存,fun函数执行完ebp就回不去了)。

接下来就正式进入了fun函数,像第一次保存完地址值那样,ebp又被闲置了,所以让ebp守住fun函数的栈底。而局部变量b与c的赋值就不再多说,与main函数的执行过程相同,当走到了return时,将计算出的结果赋值写入到刚空出的返回值那里。

此时的fun函数就执行完毕了,我们要恢复main函数调用fun函数的现场,继续往下执行,要想往下执行,必须将ebp回到main函数的栈底,并且找到fun函数返回的位置,然后跳转到那里。很简单,由于刚才保存了ebp的地址值,所以将地址值赋值给ebp,ebp就指向了main函数的栈底。

ebp的地址值出栈后,esp就指向了fun函数的返回地址,通过执行ret指令,把该地址值传给eip,使eip指向fun函数执行后的返回地址。

这样就恢复了现场,然后把fun函数的返回值传递给m,此时局部变量b、a和返回值已经没有价值了,把它们清出栈,现在就剩下干干净净的栈内容了。

现在执行最后一步,main函数就结束了,此时局部变量i、j也没有任何作用,做清栈操作,清理出干净的栈空间。

以上便是一个简单C程序的运行时结构。本文总结于新设计团队的《编译系统透视:图解编译原理》,图侵删。

图解简单C程序的运行时结构的更多相关文章

  1. 更为复杂C程序的运行时结构

    运行环境 win 10 企业版 1809 17763.194,MinGW V3.14 32位,Bundled V3.13.2,Bundled GDB V8.2. 在C语言中,栈的方向是从高地址向低地址 ...

  2. [快手(AAuto)学习笔记]如何让程序在运行时请求管理员权限(UAC)

    作者:ffsystem 作为(糟糕的)程序猿,习惯写代码解决一些简单事务.正常用批处理就能解决大部分工作,复杂一点用AutoIt 3. 有时候要分发给别人,就需要一个界面.外行你程序写得如何他看不懂, ...

  3. java拾遗4----一个简单java程序的运行全过程

    简单说来,一个java程序的运行需要编辑源码.编译生成class文件.加载class文件.解释或编译运行class中的字节码指令. 下面有一段简单的java源码,通过它来看一下java程序的运行流程: ...

  4. 如何让Qt程序在运行时获取UAC权限

    在pro文件中加入以下语句: QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\' ...

  5. Laravel 使用 Provider 为程序提供运行时配置服务

    需求: 配置参数存在数据库中,Model 是 aah,需要在每次运行时,程序可以在任何地方采用 config("aah.name") 的方式访问配置信息. 思路: 采用 Provi ...

  6. Java中获取类的运行时结构

    获取运行时类的完整结构 通过反射获取运行时类的完整结构 Field(属性).Method(方法).Constructor(构造器).Superclass(父类).Interface(接口).Annot ...

  7. 关于java程序在运行时出现a java exception has occured时解决方法

    错误截图: 出现情况原因分析: 1.环境没有配置好,配置java环境变量: 参考 检查是否正确,java javac,可以尝试重新 2.查看使用的jdk版本是否存在版本问题: 例如jdk1.7对中文的 ...

  8. c#+handle.exe实现升级程序在运行时自动解除文件被占用的问题

    我公司最近升级程序经常报出更新失败问题,究其原因,原来是更新时,他们可能又打开了正在被更新的文件,导致更新文件时,文件被其它进程占用,无法正常更新而报错,为了解决这个问题,我花了一周时间查询多方资料及 ...

  9. 通过编写c语言程序,运行时实现打印另一个程序的源代码和行号

    2017年6月1日程序编写说明: 1.实现行号的打印,实现代码的读取和输出,理解主函数中的参数含义. 2.对fgets函数理解不够 3.对return(1); return 0的含义理解不够 4.未实 ...

随机推荐

  1. 【洛谷P1471】方差

    题目大意:维护一个有 N 个元素的序列,支持以下操作:区间加,区间询问均值,区间询问方差. 题解:可知区间均值和区间和有关,即:维护区间和就等于维护了区间均值.区间方差表达式为 \(\frac{\Si ...

  2. SSO后期补充理解

    sso系统的提出: 为什么会产生sso系统呢?它的作用是什么?这跟普通的登录系统有什么区别? 我们先来说说session的实现原理:session跟cookie都是用户的会话跟踪技术,为什么登录成功后 ...

  3. Java基础-变量的定义以及作用域详解

    Java基础-变量的定义以及作用域详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字面量 常量(字面量)表示不能改变的数值(程序中直接出现的值).字面量有时也称为直接量,包 ...

  4. Ubuntu下安装BeautifulSoup4

    先去下载beautifulsoup的安装包https://www.crummy.com/software/BeautifulSoup/bs4/download/4.0/ 下载完之后解压 tar -xv ...

  5. FastDFS初步认识--上传下载流程介绍

    什么是FastDFS FastDFS 是用 c 语言编写的一款开源的分布式文件系统.FastDFS 为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标,使用 F ...

  6. Spark记录-Scala记录(基础程序例子)

    import scala.util.control._ object learnning { def main(args:Array[String]):Unit={ val n:Int=10 prin ...

  7. hdu 4857 Little Devil I

    http://acm.hdu.edu.cn/showproblem.php?pid=4897 题意:给你一棵树,边的颜色要么为白色,要么为黑色,初始每条边为白色,有三种操作 1.将u-v链上面的所有边 ...

  8. bzoj千题计划175:bzoj1303: [CQOI2009]中位数图

    http://www.lydsy.com/JudgeOnline/problem.php?id=1303 令c[i]表示前i个数中,比d大的数与比d小的数的差,那么如果c[l]=c[r],则[l+1, ...

  9. 转载自知乎大神---this 的值到底是什么?一次说清楚

    你可能遇到过这样的 JS 面试题: var obj = { foo: function(){ console.log(this) } } var bar = obj.foo obj.foo() // ...

  10. 20155339 2016-2017-2 《Java程序设计》第5周学习总结

    20155339 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 使用try.catch 使用try.catch语法,JVM会先尝试执行try区块中的代码,如 ...