C++ 从源代码到可执行二进制文件的过程

C++和C语言类似,一个C++程序从源码(.cpp)到执行文件(.exe.elf.axf),有四个过程,预编译、编译、汇编、链接

C语言和C++生成可执行程序的过程

1. 预编译

  • 展开所有宏定义,删除 #define
  • 处理所有的条件预编译指令,如 #if#ifdef
  • 处理 #include 预编译指令,将被包含的头文件插入到该预编译指令的位置;
  • 过滤所有的注释;
  • 添加行号和文件名标识。

这个过程得到不包含#指令的 .i 文件

2. 编译

  • 词法分析:将源代码的字符序列分割成一系列的记号。

  • 语法分析:对记号进行语法分析,产生语法树。

  • 语义分析:判断表达式是否有意义。

  • 中间代码优化:代码层面的优化,不依赖于具体的机器。

    删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

  • 目标代码生成:生成汇编代码。

  • 目标代码优化:机器层面的优化,同机器的硬件结构密切相关。

    充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。

    根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)对指令进行一些调整,使得目标代码更短,执行的效率更高。

这个过程将 .i 文件转换为 .s文件。

模板(template)和内联(inline)在大多数编译器中都是在编译阶段进行处理。template在编译阶段完成具现化。inline在编译阶段将函数调用替换为函数本体,从而减少函数调用的开销,注意多次调用inline函数有可能产生代码膨胀。inline只是一个申请,编译器可以拒绝太复杂的内联申请。比如带有循环、递归的函数,以及所有的虚函数,因为虚函数在运行时才能确定函数本体

3. 汇编

将汇编代码转换成机器码,生成目标文件(.obj.o),使得机器能够直接执行。

目标文件由段组成,通常至少有两个段:

  • 代码段:包换主要程序的指令。该段是可读和可执行的,一般不可写

  • 数据段:存放程序用到的全局变量或静态数据。可读、可写、可执行。

这个过程将 .s 文件转化成 .o 文件。

4. 链接

将不同的源文件产生的目标文件进行链接,生成一个可以执行的程序。

这个过程将 .o 文件转化成可执行的文件(.exe.elf.axf)。

由汇编程序生成的目标文件并不能立即执行,还要通过链接过程。

原因:

1) 某个源文件调用了另一个源文件中的函数或常量

2) 在程序中调用了某个库文件中的函数

链接分为静态链接和动态链接。

静态链接

在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算把静态库删除也不会影响可执行程序的执行;生成的静态链接库,Windows下以.lib 为后缀,Linux下以 .a 为后缀。

动态链接

在链接的时候没有把调用的函数代码链接进去,而是在执行的过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行程序就不能运行。生成的动态链接库,Windows下以 .dll 缀,Linux下以 .so 为后缀。

参考资料

https://www.cnblogs.com/mickole/articles/3659112.html
https://segmentfault.com/a/1190000020240898
https://zhuanlan.zhihu.com/p/45402323
https://www.nowcoder.com/study/live/690/2/2