C++ - 编译器工作原理


编译器可以阅读以某一种语言(源语言)编写的程序,并把该程序翻译成一个等价的、用另一种语言(目标语言)编写的程序。
C++编译系统将一个程序转化为可执行程序的过程包含:


1. 预处理

预处理器是在程序源文件被编译之前根据预处理指令对程序源文件进行处理的程序。
以#号开头标识,末尾不包含分号。
预处理命令不是C/C++语言本身的组成部分,不能直接对它们进行编译和链接。
C/C++提供的预处理功能主要有文件包含宏替换条件编译等。

1.1 头文件包含

  • 头文件是一种文本文件,以扩展名.h(.hpp)保存。
  • 头文件中一般放一些重复使用的代码,例如函数声明、变量声明、常数定义、宏的定义等等。
  • 当使用预处理指令#include引用头文件时,相当于将头文件中所有内容,复制到include处。
#include <xxx.h>    // 直接到系统目录中去找某些头文件。
#include "xxx.h"    // 先到源文件所在文件夹去找,然后再到系统目录中去找某些头文件。

1.2 宏替换

  • 一般用一个短的名字代表一个长的代码序列。
  • 宏定义包括:无参数宏定义带参数宏定义
  • 宏名和宏参数所代表的代码序列可以是任何意义的内容,如类型、常量、变量、操作符、表达式、语句、函数、代码块等。
  • 宏替换首先将源文件中在宏定义随后所有出现的宏名均用其所代表的代码序列替换之,如果是带参数宏则接着将代码序列中的宏形参名替换为宏实参名。
  • 宏替换只作代码字符序列的替换工作,不作任何语法的检查,也不作任何的中间计算,一切其它操作都要在替换完后才能进行。如果宏定义不当,错误要到预处理之后的编译阶段才能发现。
  • 如果一个宏定义中代码序列太长,一行不够时,可续行。续行是在键入回车符之前先键入符号\,注意回车要紧接在符号\之后,中间不能插入其它符号,当然代码序列最后一行结束时不能有\。

1.3 条件编译

  • 一般情况下,在进行编译时对源程序中的每一行都要编译,但是有时希望程序中某一部分内容只在满足一定条件时才进行编译,如果不满足这个条件,就不编译这部分内容,这就是条件编译。

  • 条件编译主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到多个版本控制、防止对文件重复包含的功能。if, #ifndef, #ifdef, #else, #elif, #endif是比较常见条件编译预处理指令,可根据表达式的值或某个特定宏是否被定义来确定编译条件。

  • 此外,还有 #pragma 指令,它的作用是设定编译器的状态或指示编译器完成一些特定的动作。


2. 编译

通过词法分析语法分析与形式语言和自动机有关),在确认所有指令都是符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码


3. 汇编

对于被翻译系统处理的每一个C/C++语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标机器语言代码。目标文件由段组成,通常一个目标文件中至少有两个段:代码段数据段

  • 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
  • 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

4. 链接

链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够按操作系统装入执行的统一整体。主要有静态链接和动态链接两种方式:

  • 静态链接:在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,程序运行的时候不再需要静态库文件。
  • 动态链接:把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。

这里的库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll),所谓静态、动态是指链接方式的不同。要注意静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。

扫描二维码关注公众号,回复: 4859427 查看本文章

g++(gcc)编译选项

  • -shared :指定生成动态链接库。

  • -static :指定生成静态链接库。

  • -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。

  • -L. :表示要连接的库所在的目录。

  • -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。

  • -Wall :生成所有警告信息。

  • -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。

  • -g :编译器在编译的时候产生调试信息。

  • -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件)

  • -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

  • -S:只激活预处理和编译,就是指把文件编译成为汇编代码。例子用法 gcc -S hello.c他将生成.s的汇编代码,你可以用文本编辑器察看

猜你喜欢

转载自blog.csdn.net/weixin_39129504/article/details/86217043