程序的翻译环境和执行环境详解

一. 什么是程序的翻译环境和执行环境

在任何一种标准C程序的实现中,都存在两种不同的环境

  1. 翻译环境 -- 在这个环境中代码被翻译为可执行的机器指令
  2. 执行环境 -- 用于实际执行代码

二. 详解编译+链接

每一个可执行程序的生成,都一定要经过编译和链接两部分。其中,编译又可细分为预编译(预处理)、编译和汇编三部分。编译和链接在翻译环境中完成,源文件经过编译和链接后生成可执行文件(.exe文件)。在执行环境中运行可执行文件,即可得到程序运行的结果。

图2.1  程序运行结果生成的图解

2.1 程序的翻译环境

图2.2  翻译环境图解
  • 一个项目中可能会包含多个源文件,每个源文件单独经过编译器的编译,生成对应的目标文件(.obj)。
  • 连接器将每个源文件生成的目标文件进行捆绑,同时,引入标准C库中任何被该程序用到的函数。
  • 链接器还可以将程序员个人库中的函数链接到程序中。

2.2 编译阶段

编译可细分为预编译(预处理)、编译、汇编三个阶段。

2.2.1 预编译(预处理)

预编译生成test.i文件,完成下面3项工作

  1. 完成头文件的包含#include
  2. 完成#define定义的标识符和宏的替换
  3. 删除注释内容
  4. 条件编译

2.2.2 编译

将C语言代码转换为汇编语言代码,生成test.s文件。编译阶段具体工作包括:

  • 词法分析:将字符序列转化为单词序列的过程。
  • 语法分析:在词法分析的基础上将单词序列组合成各类语法短句。如识别for循环、switch选择语句等。
  • 语义分析:在结构正确的源程序上进行上下文有关性质的审查。重点审查语义有无错误,为代码生成阶段收集类型信息。如:int arr[10], b; b = arr * 10这两条语句存在变量类型不匹配的情况,语义审查会发现其中的错误。
  • 符号汇总:通俗理解,符号汇总就是收集每个源文件中的全局符号。全局符号是在程序运行之前就已经存在的符号,而不是在程序运行起来才创建的符号(局部变量符号)。

演示代码2.1中包含两个源文件(add.c和test.c)。在test.c中,存在三个全局符号Add、main以及printf,在Add.c中,存在一个全局符号Add。x、y是局部变量,在程序运行起来后才创建符号,因此符号汇总不会汇总x和y符号。

演示代码2.1:

Add.c文件:
int Add(int x, int y)
{
	return x + y;
}


test.c文件:
extern int Add(int x, int y);

int main()
{
	int x = 2;
	int y = 3;
	int ret = Add(x, y);
	printf("ret=%d", ret);
	return 0;
}

2.2.3 汇编

汇编完成后生成目标文件,在Windows环境下,目标文件后缀名为.obj,在Linux gcc编译环境下后缀名为.o。其中test.o(test.obj)文件是elf格式文件,elf格式文件是以段表的形式组织的,应使用工具readelf阅读elf格式的文件。

什么是段表?

在分段式管理系统中,每个进程都有一个或多个对应的逻辑段,系统为每个逻辑段都建立了一张段表,段表记录了进程中每一个段在内存中的起始地址(又称基址)、段号和段的长度。

图2.3 段表

在汇编阶段完成两项工作:

  • 将汇编代码转换为机器指令
  • 生产符号表 

其中,生成符号表就是将编译阶段汇总的符号以及符号所对应的地址进行汇总,一张符号表包含符号名称和符号的存储地址。

注意:源文件中可能存在无法单独通过当前符号所在的源文件找到存储地址的符号,如演示代码2.1源文件test.c中的符号Add,由于其是外部符号,Add函数没有在源文件test.c中定义,因此编译阶段无法在处理test.c时获取符号Add的地址。

对于此类符号,符号表中会暂时存储一个虚拟地址,等待链接阶段进行符号表合并及重定位工作时进行处理(本文后面会进行讲解)。

图2.4 符号表图解

2.3 链接阶段

链接阶段将多个目标文件和链接库进行捆绑,具体的工作有下面两项:

  • 合并段表
  • 符号表的合并和重定位

2.3.1 合并段表

将多个源文件经编译后生成的目标文件(elf格式文件)的段表进行合并,每张相同段号位置的段合并为一个段,详见图2.5。

图2.5 合并段表图解

2.3.2 符号表的合并和重定位

在汇编阶段会生成多张符号表,在链接阶段多张符号表会被合并为一张,并且,某些符号表中可能会存储符号的虚拟地址,对于这种存储虚拟地址的符号正常情况下会在另一张符号表中找出其实际地址,这个虚拟地址将被舍弃。符号的汇总、符号表的生成以及符号表的合并与重定位图解见图2.6。(以演示代码2.1为例)

图2.6 符号的汇总、符号表的生成以及符号表的合并与重定位图解

test.c文件生成的符号表有三个符号Add、main、printf,其中Add符号记录的是虚拟地址,而在Add.c生成的符号表里有Add的实际地址。应该取有实际地址的Add,舍弃无实际地址的Add。

三. 程序的执行环境

程序执行的过程:

  1. 将程序载入内存。在有操作系统的环境中,一般由操作系统完成(如VS2019集成编译环境),在独立环境中程序必须手工载入(如单片机)。
  2. 开始从main函数执行程序。程序运行过程中,函数会使用运行堆栈,存储函数的局部变量和返回地址。
  3. 程序终止执行。一般通过main函数的返回值告知系统程序是正常终止还是异常终止,main函数返回0表示程序正常终止,main函数返回非零值表示程序异常终止。

全文结束,感谢大家的阅读,敬请批评指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值