编译和链接
对于平常的应用程序开发,我们很少需要关注编译与链接过程。因为通常的开发环境都是流行的集成开发环境(IDE),比如Visual Studio等。这样的IDE一般都将编译和链接的过程一步完成,通常这种编译和链接合并到一起的过程为构建(Build)。但是在这样的开发过程中,我们往往会被这些复杂的集成工具所提供的强大功能所迷惑,很多系统软件的运行机制与机理被掩盖,其程序的很多莫名其妙的错误让我们无所适从,面对程序运行时种种性能瓶颈我们束手无策。如果能够深入了解这些机制,那么解决这些问题就能够游刃有余,收放自如了。
被隐藏的过程
1 |
|
在Linux下,当我们使用GCC来编译Hello World程序时,只需要最简单的命令我们便可以在屏幕上打印出“Hello World”字样
1 | $ gcc hello.c |
事实上,上述过程可以分解为以下四个过程:
)
- 预处理(Prepressing)
- 编译(Compilation)
- 汇编(Assembly)
- 链接(Linking)
预编译
预编译过程主要处理那些源代码文件中以“#”开始的预编译指令。比如#include
、#define
等,主要的处理规则如下:
- 将所有的
#define
删除,并且展开所有的宏定义。 - 处理所有条件预编译指令,比如
#if
、#ifdef
、#elif
、#else
、#endif
。 - 处理
#include
预编译指令,将被包涵的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他的文件。 - 删除所有的注释
//
和/**/
。 - 添加行号和文件名标识,比如
#2 hello.c 2
,以便编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。 - 保留所有的
#pragma
编译器指令,因为编译器要使用它们。
我们可是使用如下的命令,将源代码文件hello.c
和相关的头文件如stdio.h
等被预编译器cpp
预编译成一个.i
文件。
1 | $ gcc -E hello.c -o hello.i |
其hello.i
文件如下所示:
1 | # 1 "hello.c" |
编译
编译过程就是把预处理完成的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。上面的编译过程相当于如下的命令:
1 | $ gcc -S hello.i -o hello.s |
汇编代码如下:
1 | .section __TEXT,__text,regular,pure_instructions |
汇编
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。上述汇编过程我们可以用一下命令完成:
1 | $ as hello.s -o hello.o. // as是汇编器 |
我们可以使用objdump -d hello.o
命令来查看 .o
文件,如下:
1 | hello.o: file format mach-o 64-bit x86-64 |
链接
通常我们要将一大堆文件链接起来才可以得到a.out
文件,即最终的可执行文件。我们可以通过以下命令来获取所有的编译选项:
1 | $ gcc hello.c --verbose // 或 gcc -v hello.c |
输出的编译信息含义可参考gcc-verbose。
参考资料
- 程序员的自我修养-链接,装载与库
- 本文标题:编译和链接
- 创建时间:2023-09-08 13:14:24
- 本文链接:2023/09/08/编译和链接/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
评论