C 语言是一种高级语言,并且是编译型的语言。即一个用 C 写出来的源文件形式的代码,是可以被我们读懂的,但是如果想让机器也能读懂认识,就必须被转换为能够被机器认识的格式--机器语言指令。而完成这个转换工作的,就是 C 编译器。编译的过程如下图所示。
从上图可以看出,将一个 C 文本源文件,转换到一个可执行目标程序,一共需要经过四个阶段。
1. 预处理阶段
预处理器查找源文件中以‘#’开始的行,并以实际的代码替换掉这些以‘#’开始的命令。比如, hello.c 中假设第一行为 #include <stdio.h> ,预处理器在读到这里的时候,会用实际的 stdio.h 文件的内容替换掉 #include <stdio.h> 这一行代码,而得到一个新的 C 源文件,即 hello.i 。
2. 编译阶段
在该阶段,将文本文件 hello.i 翻译成文本文件 hello.s , hello.s 是一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切的描述了一条低级机器语言指令。因此,现在虽然有各种各样的编程语言,但编译时,最终都是要被翻译为通用的汇编语言的。
3. 汇编阶段
这个时候,汇编器就会将 hello.s 翻译为机器语言指令了,并把这些指令打包成可重定位的目标程序格式 hello.o 。这个文件是二进制的文件,它的字节编码就是机器语言的指令了,而不是字符了。
4. 链接阶段
在 hello.c 程序中,我们调用了标准库里的函数, printf ,而 printf 函数存在于 printf.o 的目标文件中,这样的话,我们就必须提供某种方法,将 printf.o 加入到 hello.o 中。而这个工作就是由连接器完成的。连接器将所有用到的相关的 .o 都链接成一个可执行的目标文件,这个目标文件加载到 memory 以后,就可以执行了。
(参考《深入理解计算机系统》)