C/C++ 编译与链接
编译与链接主要为下述步骤,其中前三个步骤就是广义上的编译。
- 预处理:把一个
.c源文件处理成.i预处理文件 - 编译:把
.i预处理文件进一步处理成.s汇编文件(狭义上的编译) - 汇编:把
.s汇编文件最终处理得到.o机器码文件 - 链接:把多个
.o机器码文件链接成可执行文件
准备源代码
为了演示这个过程,我编写了以下两个文件作为源代码。
1 | // myheader.h |
1 | // main.c |
接下来,我将以此文件为基础进行演示。
编译(广义上的编译)
广义上的编译指的是从高级编程语言的代码到二进制机器码的过程。
预处理
预处理会对 # 开头的预处理语句进行处理。
通过 gcc -E main.c -o main.i 命令将 main.c 预处理成 main.i。
main.i 文件内容如下:
1 | # 0 "main.c" |
可以看得出来 #include 和 #define 这些预处理指令,都已经成功地执行。
#include成功地把myheader.h引入到了main.c中#define的宏定义也完成了文本替换- 源代码中的注释也全部被去掉了
编译(狭义上的编译)
狭义上的编译指的是把预处理文件编译成汇编代码的过程。
通过 gcc -S main.i -o main.s 命令将 main.i 编译成 main.s。
main.s 文件内容如下:
1 | .file "main.c" |
可以看得出来预处理好的文件已经被编译成了汇编代码。
懂一些汇编的话,应该能看出来 movl $1665, -4(%rbp) 这一行汇编代码就是给变量 i 赋值,rbp 寄存器偏移 -4 的位置就是 i 的地址。
汇编
汇编指的是从汇编代码到机器码这个过程,汇编代码和机器码指令是一一对应的。
通过 gcc -c main.s -o main.o 命令将 main.s 汇编成 main.o。
main.o 就是二进制机器码文件,由于汇编代码和机器码一一对应,我们可以从汇编代码知道 main.o 中暴露了 main 这个符号,并且标记了 main 是一个函数。在后续链接的时候,因为 C 语言中 main 函数的特殊性,它将会被处理为整个二进制程序的入口。
如果我们的代码是作为库提供给其他人使用的话,可能就不会有 main 函数,也不会进入下面的链接环节,而是把多个 .o 文件打包成一个文件,这个打包好的文件也就是库文件,根据打包过程的不同可以分为动态库和静态库。
链接
最后我们可以使用 gcc main.o -o main 将编译获得的多个 .o 文件链接成为可执行程序。在本文的例子中,只有一个 .o 文件。
最终 main 就是可执行程序。
目录结构
1 | test/ |