从源码到可执行程序
从源码到可执行程序
1.概述
2.预编译
3.编译
4.汇编
5.链接
1.概述
很多编译型语言的源码都需要被编译之后才可以正常的运行,如常用的c语言,它需通过编辑器的编译把它变成0101序列的机器语言才可以在指定机器上运行。而常见的java语言则属于解释型语言,它虽然也需要进行编译,不过它通过java编译器编出的是字节码(.class)文件,需要java虚拟机解释(解释器)解释执行,具体流程如图1所示。
图1
解释型语言与编译型语言相比,编译型语言的运行速度则会更快,因为在运行时java虚拟机解释(解释器)还需要把字节码文件翻译成目标机器对应机器指令再执行。当然,现在的java语言加入了即时编译(just in time)的优化机制来提高字节码(.class)文件的执行速度,其实质就是把运行频率较高的代码编译成机器码再执行(相对于编译型语言,人家在编译阶段就已经把这些过程完成了),但是把字节码编译成机器语言所占用的时间还是算在运行时期(所以感觉加入优化后的java代码运行速度还是比不上c吧,不过它用这个代价换取了java语言跨平台的特点还是非常不错的)。好吧,接下来看看编译型语言是怎么被编译程机器可以执行的程序的,它经历过哪些步骤?
以一个C程序语言编写的helloWord为例。
#include<stdio.h>
int a=1;int main()
{
printf("helloWorld\n");
return0;
}
通过编译命令gcc c_1.c可以得到可执行文件a.out,执行后就会得到想要的结果:helloWorld,具体如图2所示。
图2
但是实际上,从源码到可执行文件总共需要4个步骤①预编译②编译③汇编④链接,具体过程如图3所示。
图3
2.预编译
首先,源码文件按c_1.c和相关的头文件stdio.h会预编译器编译成一个.i文件,具体过程如图4所示。
图4
在预编译过程中主要是将预编译指令(如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”、“#include”、“#define”)文件插入到源文件的对应位置。打开预编译后的文件hello.i可以看到如图5的内容。在最下面就是写好的helloWorld程序,而上面部分是stdio.h文件内容。
图5
3.编译
编译过程就是把预编译完的文件进过一系列的词法分析、语法分析、语意分析及相关的优化后生成的汇编文件.s,具体生成过程可以参考《编译原理》===>??(这部分是程序程序构建的关键,反正感觉会比较难,以前本科学习编译原理的时候也只是考试背题然后通过考试最后基本忘了这门课程讲的什么东西。看来要深入学习计算机科学相关知识,编译部分也是非常重要的)??
图6
通过vim打开编译后的文件hello.s,如图8所示可以看到里面全是汇编代码。
图7
4.汇编
汇编过程就是把图7中的汇编语言按照汇编指令和机器指令一一对应的关系翻译成机器指令,具体如图8所示。
图8
5.链接
这阶段就是把汇编后的机器指令集变成可以直接运行的文件,而对目标文件进行链接主要是因为在目标文件中可能用到了在其他文件当中定义的字段(或者函数),通过链接来把多个不同目标文件关联到一起。比如有2个目标文件a和b,在 b中定义了一个函数"method",而在文件a中则使用到了b文件中的函数"method",通过链接文件a才能调用到函数"method",不然文件a根本就不知道到函数"method"底做了些什么操作。查看链接后的文件a.out如图9所示。
图8