<<程序员的自我修养 - 链接 装载与库>>是用hellowolrd开始说起的,我也不能免俗,也从helloworld说起.(又是一个helloworld,自从与编程结缘,就和helloworld挂钩了.)
编译的过程可以分为四个阶段,分别是:Preprocessed(预处理), Compilation(编译), Assembly(汇编), Linking(链接).
在不同的阶段,需要使用不同的工具处理.举个例子,在使用gcc编译程序的时候,最简单的命令就是: gcc helloworld.c
背后的机制是: gcc会调用工具执行预处理,编译,汇编,链接,最后产生二进制可执行文件.借用一句简单的话解释就是:
gcc这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译程序ccl, 汇编器as,链接器 ld.
书中有一幅图,用于解释这个过程是很好的.我画了一下:
这个图看起来比较好理解,至少比文字要清晰很多.
现在已经解释了编译期间做了哪些事情,现在进入编译的一些细节,然后看看到底发生了什么.首先给出我写的测试代码.(很简单,只是个helloworld而已.)
在查看细节之前,先看一下gcc命令的帮助手册.我在ubuntu下面,所以直接man gcc. gcc 使用的时候参数很多,所以手册很长,只看关心的内容.
(这些参数的解释都比较简单,所以很好理解.)
一、preprocessed 预处理阶段.
预处理阶段是对源码中含有"#"预处理指令进行处理.
想要具体了解预处理过程都做了写什么,需要使用gcc -E helloworld.c,使用-E参数在预处理猴输出到标准输出.为了方便,我输出到文件,所以添加-o参数.
gcc -E helloworld.c -o helloworld.i
预处理之后在生成的预处理文件helloworld.i中,会生成大量上面的代码.
预处理做的事情主要如下:
1 将所有的"#define"删除,并且展开所有宏定义. 2 处理所有的条件编译指令. "#if" "#ifdef" "#elif" "#else" "#endif" 3 处理"#include"预编译指令,将被包含的文件插入到该预编译指令的位置.该过程是递归执行的 ,因为被包含的文件可能还包含其他文件. 4 删除所有的注释. "//" 和 "/* */" 5 添加行号和文件标识,以便于编译时编译器产生调试用的行号信息用于编译时产生的编译错误 或警告时能够显示行号. 6 保留所有的#pragma编译器指令,该指令编译器需要使用.
二、Compilation 编译阶段.
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化 后产生相应的汇编代码文件.
获得汇编代码文件有两种执行命令的方法:
gcc -S helloworld.c -o helloworld.s gcc -S helloworld.i -o helloworld.s
两种方法本质上都是一样的.
(汇编,没学过,看不懂..... )
三、Assembly 汇编阶段.
汇编阶段就是将汇编代码转成机器可以执行的指令
机器在执行动作的时候需要知道的就是机器指令,而汇编语句就对应这机器指令.所以汇编阶段将我们的代码翻译成为机器可以执行的指令.
机器只要对照着机器指令对照表,翻译后就可以执行了.由于汇编阶段产生的object files中保存的是机器指令,所以在磁盘上的存储方式是二进制字节码.所以我们可以使用vi或者是
vim查看.要不然会出现乱码,看不到.
as helloworld.s -o helloworld.o or gcc -c helloworld.c -o helloworld.o
四、Linking 链接阶段.
关于链接阶段的处理,在以后再补充.