预编译到链接,从.o到.c
预编译->编译->汇编->链接
一.main.c不能直接运行,经过的步骤有哪些
二.为什么main.o不能直接运行
一.在linux下,使用gcc编程时,可以使用命令 gcc main.c 一步完成,其实,这个过程分为四个步骤:
首先,我们在main.c 文件中写了一个程序(源代码):
通过这个程序,我们来了解一下这四个步骤。
1.预编译
预编译:处理那些源代码中以“#”开始的预编译指令
使用命令
让我们看看新生成的main.i文件里面是什么
里面展开了所有的宏定义,并删除了注释,并保留了# pragma(防止重定义),添加了行号和文件标识
2.编译
编译:把预处理完的文件进行一些列的词法分析,语法分析,语义分析以及优化后产生相应的汇编代码文件
使用命令
打开新生成的main.s 文件
实际上gcc这个命令只是后台程序的包装,他会根据不同的参数要求去调用预编译编译程序cc1,汇编器as,链接器id。(对于c程序来说,这个预编译和编译的程序是cc1,对于c++来说,有对应的程序叫做cc1plus……)
3.汇编
汇编:根据汇编指令和机器指令的对照表一一翻译,每个汇编语句几乎都对应一条机器指令(编译器是将汇编代码转变成机器可以执行的命令)
使用命令
此时生成的main.o文件,我们试着运行
也就是说,新生成的main.o文件是不能运行的。
打开新生成的main.o文件。我们可以看到
里面包含了很多看不懂的符号语言,接下来我们看看链接这个步骤
4.链接
链接:把各个模块之间相互引用的部分都处理好,是的各个模块之间能够正确衔接。
使用命令
我们再来运行一下新生成的main 文件
也就是说,链接生成的文件是可执行的。
二.为什么main.o文件不能直接运行
从前面的汇编开始说起,经过一系列的扫描,语法分析等过程,源代码被编译成目标文件,但是变量的地址还没有确定,要在机器上执行的话,如果变量和上面的源代码定义在一个编译单元里,就可以为其分配空间,确定地址,如果定义在其他程序模块,那么定义在其他模块的全局变量和函数在最终运行时的绝对地址都要在最终链接时才能确定。
静态链接:目标文件(也叫做中间目标文件)和库一起链接成最终的可执行文件
链接过程:主要包括了地址和空间分配,附号决议和重定位等这些步骤
我们来通过一个例子说明:例如我们在main.c中调用fun.c函数,那么在main.c每一处调用fun.c时都必须确切知道fun这个函数的地址,所以暂时把这些调用fun函数指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正(在链接过程中,对其他定义在目标文件中的函数调用指令需要被重新调整,对其他定义在替他目标文件的变量来说也存在同样的问题,这个地址修订的过程被叫做重定位)
——参考资料《程序员的自我修养–链接,装载与库》