C++的编译过程主要分成四个部分:预处理、编译、汇编、链接。
预处理
预处理做了哪些工作?
①宏替换:将宏替换为真实的宏体,比如 程序性中有使用NUM这个宏,这个宏的定义为#define NUM 100,程序中所有的NUM都会被替换为100。
②包含头文件 将include包含的.h头文件内容,全部复制到.c文件中,因为头文件中定义了类型、宏、函数声明等等,这些都是函数调用会用到的,你调用某个函数时,就必须包含这个函数要的头文件。
疑问:头文件那么多内容,都包含进去的话,不会太多了吗?
编译时,编译器只使用要用的东西,用完后包含的内容都会被丢弃,实际上并不占空间。
③条件编译 处理#if #endif 这类的东西
④处理一些特殊的预处理关键字
最后生成.i文件,也就是扩展后的.c文件
编译
编译做了什么?
将c语法的c源码,翻译为汇编语法的汇编源码。
将.i文件生成.s文件。
.s是ascii码文件。因为汇编也是人能看懂的文字编码形式,所以.s汇编文件也是ASCII码文件。
汇编
汇编做了什么?
将ASCII的汇编源码,翻译为能够被CPU执行的机器指令,.o文件中放的就是机器指令。但是.o文件还无法运行,需要链接后才能运行。
.o文件是纯二进制文件。因为.o中放的是纯二进制的机器指令,所以我们打开后看不懂。
链接
链接(连接)做了什么
①将众多的.o合成一个完整的可执行文件。 .o实现相互依赖的,比如a.o中调用的函数,被定义在了b.o中,如果不链接在一起的话,是无法工作的。
②链接时,需要加入额外的启动代码。这个启动代码并不是我们自己写的,main函数是由启动代码调用的,我们的程序是从启动代码开始运行的。
③链接为一个可执行文件时,需要进行符号解析和地址重定位。这里就设计到静态链接和动态链接。
静态链接和动态链接两者最大的区别就在于链接的时机不一样,静态链接是在形成可执行程序前,而动态链接的进行则是在程序执行时。
静态链接的缺点很明显,一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;另一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。下面简单介绍动态链接的过程:
假设现在有两个程序program1.o和program2.o,这两者共用同一个库lib.o,假设首先运行程序program1,系统首先加载program1.o,当系统发现program1.o中用到了lib.o,即program1.o依赖于lib.o,那么系统接着加载lib.o,如果program1.o和lib.o还依赖于其他目标文件,则依次全部加载到内存中。当program2运行时,同样的加载program2.o,然后发现program2.o依赖于lib.o,但是此时lib.o已经存在于内存中,这个时候就不再进行重新加载,而是将内存中已经存在的lib.o映射到program2的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。
动态链接的优点显而易见,就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;另一个优点是,更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。但是动态链接也是有缺点的,因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。
网友评论