💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
[TOC] # 程序编译链接原理 **预处理** ~~~ .c -> .i gcc -E hello.c -o hello.i ~~~ 展开头文件宏替换 **编译** ~~~ .i / .c -> .s gcc -S hello.i -o hello.s ~~~ c的源代码变成汇编 **汇编** ~~~ .s -> .o gcc -c hello.s -o hello.o ~~~ 汇编变为二进制文件 **链接** ~~~ .o -> 可执行程序app gcc hello.o -o app ~~~ 二进制文件变为可执行文件 # 编译 ![](https://box.kancloud.cn/ff8807f0bb796ccda4788ce370e164d6_1348x436.jpg) linux下用ldd 可执行文件名 看一个可执行文件依赖哪些库 window下可以用Dependency这个工具来看一个可执行文件依赖哪些库 ![](https://box.kancloud.cn/b915cb9e79ce4ac597903ea2f1c731c5_834x383.png) ![](https://box.kancloud.cn/df03fb502c42153ad42581e662e34029_740x198.png) 1. 预处理:宏文件展开,头文件展开,条件编译等,同时将代码中的注释删除,这里并不会检查语法 2. 编译: 检查语法,将预处理后文件编译生成汇编文件 3. 汇编: 将汇编文件生成目标文件(二进制文件) 4. 链接: C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终可执行程序中去 ~~~ 预处理: gcc -E hello.c -o hello.i 编译: gcc -S hello.i -o hello.s 汇编: gcc -c hello.s -o hello.o 链接: gcc hello.o -o hello_elf ~~~ | 选项 | 含义 | | --- | --- | | -E | 只进行预处理 | | -S(大写) | 只进行预处理和编译 | | -c(小写) | 只进行预处理,编译和汇编 | | -o file | 指定生成的输出文件名为file | 在 Visual Studio 中,不用进行任何设置就可以在工程目录下看到 demo.asm 文件 # 预处理 预处理过程主要是处理那些源文件和头文件中以`#`开头的命令,比如 #include、#define、#ifdef 等。预处理的规则一般如下: * 将所有的`#define`删除,并展开所有的宏定义。 * 处理所有条件编译命令,比如 #if、#ifdef、#elif、#else、#endif 等。 * 处理`#include`命令,将被包含文件的内容插入到该命令所在的位置,这与复制粘贴的效果一样。注意,这个过程是递归进行的,也就是说被包含的文件可能还会包含其他的文件。 * 删除所有的注释`//`和`/* ... */`。 * 添加行号和文件名标识,便于在调试和出错时给出具体的代码位置。 * 保留所有的`#pragma`命令,因为编译器需要使用它们。 预处理的结果是生成`.i`文件。`.i`文件也是包含C语言代码的源文件,只不过所有的宏已经被展开,所有包含的文件已经被插入到当前文件中。当你无法判断宏定义是否正确,或者文件包含是否有效时,可以查看`.i`文件来确定问题。 在 GCC 中,可以通过下面的命令生成`.i`文件: $gcc -E demo.c -o demo.i `-E`表示只进行预编译 在 Visual Studio 中,在当前工程的属性面板中将“预处理到文件”设置为“是”,如下图所示: ![](https://box.kancloud.cn/2b0714b177b016679c3b4d57313aa307_701x504.png) 然后点击“运行(Run)”或者“构建(Build)”按钮,就能在当前工程目录中看到 demo.i # 汇编(Assembly) 汇编的过程就是将汇编代码转换成可以执行的机器指令。大部分汇编语句对应一条机器指令,有的汇编语句对应多条机器指令 汇编过程相对于编译来说比较简单,没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编语句和机器指令的对照表一一翻译就可以了。 汇编的结果是产生目标文件,在 GCC 下的后缀为`.o`,在 Visual Studio 下的后缀为`.obj` # 链接(Linking) 目标文件已经是二进制文件,**与可执行文件的组织形式类似,只是有些函数和全局变量的地址还未找到,程序不能执行**。链接的作用就是找到这些目标地址,将所有的目标文件组织成一个可以执行的二进制文件。 预处理和汇编的过程都比较简单,有了上面的介绍,相信大家很容易理解。 编译的过程最为复杂,可以细分为词法分析、语法分析、语义分析和指令优化,这里涉及到诸多算法以及正则表达式 而目标文件的结构、可执行文件的结构、链接的过程是我们要重点研究的,它能够让我们明白多文件编程以及模块化开发的原理,这是大型项目开发的基石。 最后需要说明的是:汇编的过程非常简单,仅仅是查表翻译,我们通常把它作为编译过程的一部分,不再单独提及。这样,源文件经过预处理、编译和链接就生成了可执行文件