没有输出的输入是不完整的

0%

终探c++编译过程

在科研项目中碰到了一个c++的程序,需要自己进行编译,所以就顺带着复习了一波c++的编译过程。
本篇文章探究c++编译过程的第三篇,属于本系列暂时的最后一篇,总结了本次对于整个编译过程的探究结果,同时解释了动态链接和静态链接以及g++和gcc的一些区别和联系。

其实c++编译过程很复杂,一本《编译原理》让无数从业人员感到自己能力有限,我们这里介绍的仅仅是最简单的,比较概括性的内容,如果想继续深入了解可以自行去看《编译原理》书籍。

编译过程总结


我们现在应用的g++编译器其实是一个比较成熟的工具,内部细节我们不做深入探讨。但是从总体上来讲,它将整个编译过程分成了如下的四个步骤:

预编译

g++执行编译

从源代码文件变成预处理之后的源代码文件

1
g++ -E helloword.cpp helloword.ii

原理解释

简单点说,就是将代码进行整合,包括宏定义,include等都替换进来,没用的东西都删掉,比如注释等。

详细点说
预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

  1. 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理
    1. 将所有的“#define”删除,并且展开所有的宏定义
    2. 处理所有的条件编译指令,如:“#if”、“#ifdef”、“#elif”、“#else”、“endif”等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。
    3. 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。

(注意:这个过程可能是递归进行的,也就是说被包含的文件可能还包含其他文件)
2. 删除所有的注释
3. 添加行号和文件名标识
以便于编译时编译器产生调试用的行号信息及用于编译时产生的编译错误或警告时能够显示行号
4. 保留所有的#pragma编译器指令

编译优化

g++执行编译

从源代码文件变成汇编代码

1
g++ -S helloword.cpp helloword.s

原理解释

将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

汇编

g++执行编译

从源代码文件变成对象文件

1
2
g++ -c talk.cpp -o talk.o
g++ -c talktest.cpp -o talktest.o

原理解释

将编译完的汇编代码文件翻译成机器指令,并生成可重定位目标程序的.o文件,该文件为二进制文件,字节编码是机器指令。

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译即可

链接

gcc执行编译

从对象文件变成可执行文件

1
2
g++ talk.o talktest.o -o talktest
./talktest

也可以从源代码文件变成可执行文件

1
2
g++ helloword.cpp -o helloworld
./helloworld

原理解释

通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序

关于链接过程

在编译器的第四步链接的时候,主要工作是将对象文件和标准库中的文件进行链接,打包成可执行文件,这个大家应该已经很清楚啦。那么怎么链接呢?

静态链接

静态链接是直接将所需要的库都直接嵌入到我们的主程序中,然后直接打包成可执行程序。

动态链接

在主程序中仅仅对这个库进行一次声明,等到程序执行到这个地方的时候,再去引用这个库。

共同存在的必要性

静态库有容易使用和理解,而且也达到了代码复用的目的,那为什么我们还要有动态库呢?

  1. 静态库容易造成空间浪费。
  2. 静态库对程序的更新、部署和发布页会带来麻烦

这两个缺点也就决定了动态库存在的必要性。

  1. 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。
  2. 动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

Gcc和G++区别与联系

gcc和g++都是GNU组织的编译器。
1、对于.c后缀的文件,gcc把它当做是C程序;g++ 当做是C++程序;
2、对于.cpp后缀的文件,gcc和g++ 都会当做c++程序。
3、编译阶段,g++ 会调用gcc;
4、连接阶段,通常会用g++ 来完成,这是因为gcc命令不能自动和c++程序使用的库连接

参考

  1. C/C++ 程序编译过程详解
  2. 程序编译的四个过程
    3.C/C++程序编译过程详解
    4.C++静态库与动态库