从源代码到可执行文件(.exe)的完整过程
从源代码到可执行文件(.exe)的完整过程
源代码 (.cpp/.h) ↓ 【预处理】→ 展开宏、包含头文件 ↓ 【编译】→ 生成汇编代码 (.s) ↓ 【汇编】→ 生成目标文件 (.o/.obj) ↓ 【链接】→ 合并目标文件 → 可执行文件 (.exe)linux命令行
预处理 gcc -E a.c -o a.i 编译 gcc -s a.i -o a.s 汇编 gcc -c a.s -o a.o 链接 gcc a.o -o a//gcc -o a a.o一、预处理
输入:.c/.cpp源文件
输出:.i或.ii预处理后的文本文件
预编译过程主要处理那些源代码 文件中的以“#”开始的预编译指令,比如“#include”,“#define”等,主要处理规则如下:
将所有的“#define”删除,并且展开所有的宏定义。
处理所有条件预编译指令,比如“#if”,“#ifdef”,“#elif”,“#else”,“#endif”。
处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
把include头文件的内容整个复制粘贴到这里。删除所有的注释“//”和“/**/”。
添加行号和文件名标识,比如#2“a.c”2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
保留所有的#pragma编译器指令,因为编译器需要使用它们。
二、编译
输入:.i预处理文件
输出:.s汇编文件
编译过程就是把预处理 完的文件进行一系列词法分析,语法分析,语义分析,代码优化及优化后生成相应的汇编代码文件。
代码优化的优缺点:
- 优点:提高效率
- 缺点:多线程下不可控
三、汇编
输入:.s汇编文件
输出:.o或.obj目标文件(二进制机器码)
汇编过程就是由汇编器将汇编代码转变成机器可以执行的二进制指令。
四、链接
输入:你的所有.o文件 + 静态库.lib/.a
输出:最终的.exe可执行文件(Windows下)或ELF(Linux下)
链接的主要内存就是把各个模块之间相互引用长度部分都处理好,使得各个模块之间能够正确的衔接。简单的理解为将各个目标文件链接起来生成最终的可执行文件。
链接过程可以具体的分为以下四步:
合并段和符号表,合并多个文件的符号表及各段内容,放入一个新的文件中。把所有
.o文件的代码段、数据段合并。符号解析,在每个文件符号引用(引用外部符号)的地方找到符号的定义。这就是符号解析。
地址和空间分配,符号解析成功后,为程序分配虚拟地址空间。符号重定位 // 指令段,符号重定向就是对.o文件中.text段指令中的无效地址给出具体的虚拟地址或者相对位移偏移量。
修正所有跳转指令的偏移量,确保程序运行时能跳转到正确的内存地址。
链接又分为:
静态链接
动态链接
命令行
命令行:直接调用编译器指令。所有的命令、文件名都需要自己手敲。完全不适用于大一点的工程。
Makefile
Makefile:脚本文件。负责整个工程的编译、链接等规则。所有的规则需要自己指定。写好后只需要输入make,就可以调用编译器指令自动直行四个阶段。
Makefile 不是用来“编译”的,而是用来“调度”编译四阶段的。
makefile在一些简单的工程完全可以人工手写,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。
Makefile 的麻烦,在于你必须手动把每个.cpp依赖了哪些.h,全部精确地写在文件里。在大型工程中,这种依赖关系呈几何级数增长,手写几乎不可能,所以才会有相应的工具来自动生成依赖关系。
Makefile 为什么编写起来麻烦:
语法太古老了(诞生于 1976 年)。它要求严格区分 Tab 和空格,变量展开规则晦涩。
如果换个平台makefile要重新写。跨平台性很差。
必须手动把每个
.cpp依赖了哪些.h,全部精确地写在文件里。Make 没有函数封装、模块导入这种现代特性,容易大量复制粘贴。
报错信息晦涩,缺乏“人性化”的报错。如果报错,Make 只会告诉你:Makefile:12: *** missing separator. Stop.
ninja
ninja与make一样,属于同级别的工具,功能是对工程文件及进行编译。
ninja不像Make那样试图成为一个”万能”的构建系统,而是专注于执行构建任务本身。这种设计思路让它避免了大量不必要的功能开销。
Ninja在默认情况下就会充分利用多核CPU的性能,而且它的任务调度算法相当高效。
Ninja是用C++编写的,而且特别注重减少运行时开销。它的配置文件解析速度极快,内存占用也更少。
Ninja 舍弃了各种高级功能,语法和用法非常简单,给它指定好了具体详细要做什么,所以启动编译的速度非常快。
CMake
CMake:Makefile生成器,可以跨平台。可以使用简单的语句来进行描述编译、链接等规则。
cmake是一个生成 .ninja 和 .makefile 的工具。因为担心很多人不熟悉makefile文件和ninja文件的写法,所以cmake只需要用户通过对源码文件的简单描述(就是CMakeLists.txt文件),就能自动生成一个project的makefile文件或者ninja文件,然后就可以通过ninja或者make进行启动编译了。
CMake不直接编译代码,而是生成Makefile,由Makefile进行编译。
- 命令行执行
cmake .. - CMake读取你的CMakeLists.txt,自动探测你的系统环境(编译器路径、库位置)。
- 根据你的系统,生成对应平台的Makefile(或Visual Studio的.sln工程文件)。
- 再执行
make,实际是调用刚刚生成的Makefile去编译。
源码 ↓ 【元构建系统】CMake/Meson(处理逻辑、环境探测) ↓ 输出构建脚本 【执行后端】Make / Ninja(负责真正编译) Make ←→ Ninja 【同级,都是执行后端,互相替代】 CMake ←→ Meson ←→ Autotools 【同级,元构建生成器】生成可执行程序的四个步骤——预处理,编译,汇编,链接_预处理编译4个步骤-CSDN博客
关于项目编译工具ninja、make、cmake的区别与优劣 - 我是标同学 - 博客园
Ninja为何比Make更快? – N1XON
