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

代码区与动态数据区由三个寄存器控制,分别是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. 从ACM会议分析我国计算机科学近十年发展情况

    从ACM会议分析我国计算机科学近十年发展情况 来源:<中国计算机学会通讯>2015年第10期<专栏> 作者:陈 钢 2006年,承蒙李国杰院士推荐,<中国计算机学会通讯& ...

  2. jQuery下实现等待指定元素加载完毕

    先声明下这个方法的使用场合,以免误导大家..比如在博客园,我们没法修改他的源代码,那么只能想办法监视元素的出现了.所以下面方法是在修改不了源码的情况下使用,而非写自己的项目.. 今天在改博客几个样式的 ...

  3. 利用requestAnimationFrame和Tween算法实现兼容所有浏览器的运动动画,直接秒杀Css3动画

    以下贴出Tween的代码: /* * Tween.js * t: current time(当前时间): * b: beginning value(初始值): * c: change in value ...

  4. 20155206 2016-2017-2 《Java程序设计》第7周学习总结

    20155206 2016-2017-2 <Java程序设计>第7周学习总结 教材学习内容总结 认识时间与日期 1.格林威治时间(GMT):通过观察太阳而得,因为地球公转轨道为椭圆形且速度 ...

  5. MongoDB 之 $关键字 及 $修改器 $set $inc $push $pull $pop MongoDB - 4

    我们在之前的 MongoDB 之 手把手教你增删改查 MongoDB - 2 中提到过 $set 这个系统关键字,用来修改值的对吧 但是MongoDB中类似这样的关键字有很多, $lt $gt $lt ...

  6. 工具类。父类(Pom文件)

    ego_parent(pom文件) <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...

  7. Mysql字符集介绍

  8. C++产生固定范围内的固定数量的随机数

    #include<iostream> #include<ctime> #include<random> using namespace std; void knut ...

  9. JQuery效果隐藏/显示

    hide() 方法 语法 $(selector).hide(speed,callback) show() 方法 语法 $(selector).show(speed,callback) 参数 描述 sp ...

  10. USB、UART、SPI等总线速率

    1. USB总线 USB1.1: ---低速模式(low speed):1.5Mbps ---全速模式(full speed): 12Mbps USB2.0:向下兼容.增加了高速模式,最大速率480M ...