您的位置:首页 > 编程语言

程序从代码到可执行文件的过程简述

2015-09-16 22:30 363 查看
代码编写结束后,使用IDE(集成开发环境)直接编译就可以得到可执行文件。在这个过程中,IDE进行了很多内部操作。分别为:预处理,编译,汇编,链接。之后按序简述每一个步骤的执行

1.预处理

GCC预编译的指令为:$gcc -E hello.c -o hello.i ,C++文件预编译后的扩展名为:hello.ii

预编译步骤主要操作源代码文件中以“#” 开始的预编译指令。主要处理规则如下:

a) 将所有的 “#define” 指令删除,并展开内容中的宏定义

b) 处理所有条件预编译指令,如 “#if”等

c) 处理“#include” 预编译指令,将被包含的文件插入到该预编译指令的位置,递归包含

d) 删除所有注释行

e) 添加行号与文件名标识

f) 保留所有#pragma 编译器指令

2.编译过程

GCC编译过程的指令为:$gcc -S hello.i -o hello.s 或者 $gcc -S hello.c -o hello.s

编译过程经历了扫描器(词法分析),语法分析,语义分析,源代码优化,产生目标代码,目标代码优化的过程,产生对应的汇编代码文件。

a) 扫描器:源代码被输入到扫描器中,扫描器进行简单的词法分析,运用有限状态机的算法,将源代码分割为一系列的记号(关键字,标识符,字面值和特殊符号<+,-,*,/等>)。有一个叫做 len 的程序可以实现词法扫描,按照用户之前描述好的词法规则将输入的字符串分割为一个个记号

b) 语法分析:对扫描器产生的记号进行语法分析,从而产生一棵语法树,其叶子节点通常是标识符或者字面值。有一个叫yacc的工具,可以根据用户定义的语法规则对输入的记号序列进行解析,构建出一棵语法树

c) 语义分析:语法分析完成了表达式语法层面的解析,但它并不了解这个语句是否有意义,如 C语言中,两个指针做乘法运算时没有意义的,但是这个语句在语法层面是合法的。编译器只能进行静态语义分析(如类型转换,声明匹配等),不能进行动态语法分析(如除数为0的除法操作)。经过语义分析阶段后,整个语法树的表达式都被标识了类型,如果存在类型转换,还会在语法树中插入相应的转换节点。

e)源代码优化:源代码优化器将整个语法树转换为中间代码(如三地址代码等),并对三地址代码进行优化。

举个例子,以下代码:

array[index] = (index + 4)*(2+6);
其被源代码优化器转换为三地址码如下:

t1 = 2 + 6;
t2 = index + 4;
t3 = t1 * t2;
array[index] = t3;
转为三地址代码后又进行源代码优化,优化结果如下:

t2 = index + 4;
t2 = t2 * 8;
array[index] = t2;


源代码优化产生的中间代码将编译器分为前端和后端两个部分,前端负责产生并优化与机器无关的中间代码,后端则负责则将中间代码转换为目标机器代码。到目前的操作,都属于前端部分

f) 产生目标代码:代码生成器将中间代码转换成目标机器代码(汇编语言),这个过程依赖与不同的主机,属于编译器的后端部分。因此,对跨平台的编译器而言,可以针对不同的平台使用同一个前端,而使用不同的对应平台的后端

g) 目标代码的优化:这个是对产生的汇编代码进行优化,此处不涉及汇编,不举例了

3. 汇编过程

GCC汇编过程的指令为:$gcc -c hello.s -o hello.o 或 $gcc -c hello.c -o hello.o

汇编器将汇编代码转换为机器可以执行的指令,每一个汇编语句几乎对应一条机器指令。该过程相对简单,只是根据汇编指令和机器指令的对照表一一翻译即可

4.链接过程

对C/C++等语言而言,其不同的模块(源文件)之间的通信方式有两种:一种是模块间的函数调用,另一种是模块间的变量访问。编译器在编译到目标文件的过程中,将引用别的模块中的函数与变量的地址都暂时搁置(设为0x00000000),等待链接器对这些模块进行链接时将这些含有引用的指令进行修正,调整至正确的地址。

其中,这个地址修正的过程被称为重定位,而每一个要被修正的地方称为重定位入口。链接过程包括了地址和空间分配,符号决议与重定位等步骤
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: