【Linux】编译器-gcc/g++的使用(预处理、编译、汇编、连接)

目录

01.预处理(宏替换)

02.编译(生成汇编)

03.汇编(生成机器可识别码)

04.连接(生成可执行文件或库文件)

05.选项


编译器在编译代码时包含以下四个步骤:1.预处理 2.编译 3.汇编 4.连接 下面来一一介绍

格式:gcc [选项] 要编译的文件 [选项] [目标文件]

01.预处理(宏替换)

编译器预处理阶段主要负责处理源代码中的预处理指令,其中包括宏替换。

预处理器

编译器预处理阶段由预处理器负责执行。预处理器负责源代码中以‘#’开头的预处理指令,例如‘#define’、‘#include’、‘#ifdef’等,并将它们转换为相应的代码。

宏替换

宏替换是与处理区的一项重要功能。在预处理阶段,编译器会执行宏替换,将源代码中的宏定义替换为其相应的内容。宏定义通常使用‘#define’指令定义,例如

#define PI 3.14159

在预处理阶段,编译器会将代码中所有出现的‘PI’替换成‘3.14159’。

宏参数

宏定义可以嗲有参数,这样可以创建参数化的宏。例如:

#define MAX(x, y) ((x) > (y) ? (x) : (y))

宏展开

预处理器在进行宏替换时会执行宏展开,即将宏调用替换成宏定义的内容,在进行宏展开时,会递归地展开嵌套的宏调用,直到没有更多的宏调用为止。

实例

选项-E:该选项的作用是让gcc在预处理结束后停止编译过程。

选项-o:是指目标文件,“text.i”文件为已经过预处理的C原始程序。

02.编译(生成汇编)

在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。

词法分析

也称扫描器,它负责将源代码分解成单词,去除空白字符和注释。通常使用有限状态自动机或正则表达式来实现。

语法分析

也称解析器,它负责将词法分析器生成的单词流转换成抽象语法树。这个阶段检查语法错误,确保代码符合语言规范。

语义分析

语义分析器检查源代码中的语义错误,如类型不匹配、未声明的变量等。它还负责构建符号表以及执行类型推断等任务。

优化

在生成目标代码之前,编译器通常会对中间表示进行有害,以提高程序的性能和效率。优化技术包括常量折叠、循环展开、死代码删除等。

代码生成

在这个阶段,编译器将优化后的中间表示转换成目标机器的汇编代码或机器码。这个过程通常包括指令选择、寄存器分配、指令调度等步骤。

实例

选项-S:进行编译,生成汇编代码,并停止汇编过程。

03.汇编(生成机器可识别码)

汇编阶段是把编译阶段生成的“.s”文件转成目标文件

语法分析

在语法分析阶段,编译器会将源代码转换为中间表示形式,通常是抽象语法树或者中间代码。这个过程包括词法分析和语法分析。

词法分析器负责将源代码分解为单词或者标记,去除空格、注释等不必要的字符。

代码生成

代码生成阶段将中间表示形式转换为目标机器能够识别的汇编代码或者机器码。这个阶段包括指令选择、寄存器分配、指令调度等过程。

指令选择负责将中间表示的操作转换为目标机器的指令序列。寄存器分配则是将变量分配到寄存器或者内存位置,以最小化访存操作。指令调度会对指令序列进行重新排列,以优化执行顺序,提高指令级并行度和流水线利用率。

实例

选项-c:将汇编代码转化为二进制目标代码

04.连接(生成可执行文件或库文件)

在成功编译之后,就进入了链接阶段。

实例

输入./对程序进行执行

这里涉及一个重要的概念:函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有函数的定义,那么“printf”函数是哪里实现的呢?

事实上:系统把这些函数实现都被做到名为lib.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径“/user/lib”下进行查找,也就是链接到lib.so.6库函数中区,这样就能实现函数“printf”了,而这也就是链接的作用

函数库分为静态库及动态库:

  1. 静态库

    • 静态库是一组预编译的目标文件的集合,这些目标文件中包含了函数的实现和数据。
    • 静态库的文件扩展名通常为 .a(在Unix/Linux系统中)或 .lib(在Windows系统中)。
    • 当你使用静态库时,编译器会将库文件的代码复制到可执行文件中,使得可执行文件变得更大。这意味着可执行文件中包含了静态库中的所有函数和数据,使得可执行文件变得更加独立。
  2. 动态库

    • 动态库是一组预编译的目标文件的集合,与静态库不同的是,它们在程序运行时被动态加载到内存中。
    • 动态库的文件扩展名通常为 .so(在Unix/Linux系统中)或 .dll(在Windows系统中)。
    • 使用动态库可以减小可执行文件的大小,并且可以在运行时动态地加载和卸载库文件。这使得动态库在系统资源使用和代码更新方面更加灵活。

05.选项

除了以上的四个,gcc还有很多选项:

-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面

-S  编译到汇编语言不进行汇编和链接

-c  编译到目标代码

-o 文件输出到 文件

-static 此选项对生成的文件采用静态链接

-g 生成调试信息。GNU 调试器可利用该信息。

-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.

-O0

-O1

-O2

-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高

-w  不生成任何警告信息。

-Wall 生成所有警告信息。

上一篇:Linux服务器(RedHat、CentOS系)安全相关巡检shell脚本


下一篇:【13】vue2和vue3对比