C++编译器工作原理

从源代码到可执行文件

从源代码到可执行文件主要分:预处理、编译+汇编、静态链接 这三个过程
在这里插入图片描述

  • gcc HelloWorld.c -E -o HelloWorld.i 预处理:加入头文件,替换宏。
  • gcc HelloWorld.c -S -c -o HelloWorld.s 编译:包含预处理,将 C 程序转换成汇编程序。
  • gcc HelloWorld.c -c -o HelloWorld.o 汇编:包含预处理和编译,将汇编程序转换成可链接的二进制程序。
  • gcc HelloWorld.c -o HelloWorld 链接:包含以上所有操作,将可链接的二进制程序和其它别的库链接在一起,形成可执行的程序文件。

下面分别介绍下这三个过程:

预处理:

预处理过程主要处理源文件中的以“#”开始的预编译指令。包括#include,#define, #if等等
主要的处理规则如下:

  • 将所有的#define删除,并且展开所有的宏。
    如#define a b 就是将所有的a替换成b
  • 处理所有的条件预编译指令,,如#if,#ifdef,#else,#endif,以此来决定对哪些代码进行处理,将那些不必要的代码过滤掉
  • 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置

编译+汇编

编译:
通过词法分析、语法分析、语义分析、源代码优化、类型检查等等,将源代码翻译成汇编代码。

汇编:
通过代码生成、目标代码优化等把汇编语言代码翻译成二进制机器码

编译+汇编示意图:
在这里插入图片描述

静态链接

前面的编译中,每个源代码模块是单独编译的,然而不同的模块之间不可避免地要相互引用变量或函数,这些变量或函数的地址只有在此阶段才能确定。链接过程就是把编译器生成的一个个目标文件链接成二进制可执行文件(.exe)

此阶段的链接是静态链接,运行期间的链接则是动态链接。
动态链接:静态链接期间不完成实际的链接操作,只保存函数的重定位信息,在运行期间才去找到动态库的函数符号进行重定位。

编译完成之后,运行:
“静态的程序”被载入内存,由 CPU 逐条语句执行,就形成了“动态的进程”。

静态库 vs 动态库

静态库

什么是静态库
静态库是指在应用中,有一些公共代码是需要反复使用,就把这些代码编译为库文件;在链接步骤中,链接器从库文件取得所需的代码,复制到生成的可执行文件中。

命名方式:
静态库的文件名命名方式是“libxxx.a”,库名前面加“lib”,windows和linux下后缀都用的是“.a”,“xxx”为静态库名。

链接时间:
编译过程中被载入程序

连接方式:
静态库的链接是将整个函数库的所有数据都整合进了目标代码。

优缺点:

  • 优点:可执行程序可以直接运行,不再需要外部函数库的支持,因为所有函数都已经被编进去了。运行效率较高。

  • 缺点:假如多个程序使用同一个静态库,等同于多段相同的代码分别保存在不同的可执行程序中,变相占用了更多的内存空间。而且如果所使用的静态库发生更新改变,你的程序必须重新编译。

动态库

什么是动态库
动态库就是编译程序时,可执行程序中只保存对应的函数的引用表,等到程序执行时,再链接对应的库。

命名方式:
动态库的命名方式与静态库类似,前缀相同为“lib”,linux下后缀名为“.so(shared object)”即libxxx.so;而windows 下后缀名为“.dll(dynamic link library)”即libxxx.dll.

链接时间和方式:
动态库在编译的时候并没有被编译进目标代码,而是当你的程序执行到相关函数时才调用该函数库里的相应函数。

优缺点:

  • 优点:多个可执行程序共用同一个库,节省内存。且动态库的改变并不影响 你的程序,所以动态函数库升级比较方便。

  • 缺点:因为函数库并没有整合进程序,所以程序的运行环境必须提供相应的库,运行可执行程序需要额外做一次链接操作,执行速度会相对慢一些

编译过程详解

在这里插入图片描述
更详细内容见:
编译的全过程都悄悄做了哪些事情?


版权声明:本文为qq_40337086原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。