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

代码区与动态数据区由三个寄存器控制,分别是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程序的运行时结构的更多相关文章
- 更为复杂C程序的运行时结构
运行环境 win 10 企业版 1809 17763.194,MinGW V3.14 32位,Bundled V3.13.2,Bundled GDB V8.2. 在C语言中,栈的方向是从高地址向低地址 ...
- [快手(AAuto)学习笔记]如何让程序在运行时请求管理员权限(UAC)
作者:ffsystem 作为(糟糕的)程序猿,习惯写代码解决一些简单事务.正常用批处理就能解决大部分工作,复杂一点用AutoIt 3. 有时候要分发给别人,就需要一个界面.外行你程序写得如何他看不懂, ...
- java拾遗4----一个简单java程序的运行全过程
简单说来,一个java程序的运行需要编辑源码.编译生成class文件.加载class文件.解释或编译运行class中的字节码指令. 下面有一段简单的java源码,通过它来看一下java程序的运行流程: ...
- 如何让Qt程序在运行时获取UAC权限
在pro文件中加入以下语句: QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\' ...
- Laravel 使用 Provider 为程序提供运行时配置服务
需求: 配置参数存在数据库中,Model 是 aah,需要在每次运行时,程序可以在任何地方采用 config("aah.name") 的方式访问配置信息. 思路: 采用 Provi ...
- Java中获取类的运行时结构
获取运行时类的完整结构 通过反射获取运行时类的完整结构 Field(属性).Method(方法).Constructor(构造器).Superclass(父类).Interface(接口).Annot ...
- 关于java程序在运行时出现a java exception has occured时解决方法
错误截图: 出现情况原因分析: 1.环境没有配置好,配置java环境变量: 参考 检查是否正确,java javac,可以尝试重新 2.查看使用的jdk版本是否存在版本问题: 例如jdk1.7对中文的 ...
- c#+handle.exe实现升级程序在运行时自动解除文件被占用的问题
我公司最近升级程序经常报出更新失败问题,究其原因,原来是更新时,他们可能又打开了正在被更新的文件,导致更新文件时,文件被其它进程占用,无法正常更新而报错,为了解决这个问题,我花了一周时间查询多方资料及 ...
- 通过编写c语言程序,运行时实现打印另一个程序的源代码和行号
2017年6月1日程序编写说明: 1.实现行号的打印,实现代码的读取和输出,理解主函数中的参数含义. 2.对fgets函数理解不够 3.对return(1); return 0的含义理解不够 4.未实 ...
随机推荐
- python之旅:网络编程
一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端为你提供视频 ...
- C++11 & C++14 & C++17新特性
C++11:C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto.decltype,和模板的大量改进. 新的关键字 auto C++11中引入auto第一种作用是为了自动类型推导 ...
- Spark记录-spark报错Unable to load native-hadoop library for your platform
解决方案一: #cp $HADOOP_HOME/lib/native/libhadoop.so $JAVA_HOME/jre/lib/amd64 #源码编译snappy---./configure ...
- 20155310 2016-2017-2 《Java程序设计》第五周学习总结
20155310 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 •收集对象的行为,像是新增对象的add()方法.移除对象的remove()方法等,都是定义在 ...
- yum安装失败:ublic key for **.rpm is not installed
yum install mysql-server --nogpgcheck package_need_to_install
- keyspace notification(键空间通知)-待验证
一.需求分析: 设置了生存时间的Key,在过期时能不能有所提示? 如果能对过期Key有个监听,如何对过期Key进行一个回调处理? 如何使用 Redis 来实现定时任务? 二.序言: 本文所说的定时任务 ...
- Tomcat不同版本所对应的Servlet/JSP规范
上午在别人机器上做演示,写好 Servlet居然访问不了.后来回想叻下,觉得应该是Tomcat版本不一致的问题,我用的是Tomcat 7.0建的Project,他们大多数都是用的6.0的版本.回来又仔 ...
- mybatis-config.xml 模板
ssm模板 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration ...
- pyhton之os.path
目录结构 file __file__表示了当前文件的path 以相对路径运行:python 1.py 结果:1.py 以绝对路径运行:python F:\python-study\test\1.py ...
- Explain之key_len长度计算
以前我很好奇通过执行计划Explain去分析SQL的时候看到的key_len值有时很小,有时看到很大,那时也不知道它是怎么算出来的,现在终于搞懂了,嘻.因为网上对key_len的长度的计算的资料也很少 ...