0%

cpp编译过程

cpp 编译过程

一、预编译

该过程主要是根据预处理指令(#include、#define、#if等)重新组装c++代码,经过预处理阶段,将产生一个没有注释、没有include、没有define、没有条件编译(#if/#else)等指令的.i文件

可以使用 g++ -E main.cpp -o main.i 生成 .i 文件

  • include的头文件拼接进来

  • define宏替换

  • if 等条件替换

  • 保留#pragma编译器指令(非预编译指令),该指令用于设置编译器状态或指示编译器完成一些特定的动作

    在预编译阶段被处理的指令亦称伪指令(如#include、#define、#if等)
    常见的#pragma用法有#pragma once、#pragma message等

  • 用#line指令(非预编译指令)强制编译器按指定的行号对源程序的代码重新编号,以便于编译时产生的错误警告能显示行号

    #line的用法一般为 #line 行号 filename,用于强制编译器按指定的行号,开始对源程序的代码重新编号,在调试的时候,可以按此规定输出错误代码的准确位置

    经过这一阶段的处理,源程序就就变成了只包含字符串和#pragma和#line的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
using namespace std;
#define ADD(x, y) (x + y)
#ifndef ONLINE_JUDGE

int a = 100;

#elif

int a = 1000;
#define ONLINE_JUDGE 1

#endif
int maxFun(int x, int y);
int maxFun(int x, int y);
int maxFun(int x, int y);
int maxFun(int x, int y)
{
return x > y ? x : y;
}
int main()
{
cout << ADD(1, 2) << endl;
cout << a << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 82 "D:/mingw64-13.1.0/lib/gcc/x86_64-w64-mingw32/13.1.0/include/c++/iostream" 3

}
# 2 ".\\05_fun1.cpp" 2

# 2 ".\\05_fun1.cpp" // 将下一行看做第2行 修改了 __FILE__ __LINE__ 内置变量
using namespace std;


int a = 100;




int maxFun(int x, int y);
int maxFun(int x, int y);
int maxFun(int x, int y);
int maxFun(int x, int y)
{
return x > y ? x : y;
}
int main()
{
cout << (1 + 2) << endl;
cout << a << endl;
return 0;
}

二、编译

编译原理:

  • 词法分析

  • 语法分析

  • 抽象语法树(AST)

  • 语义分析

  • 符号表

    符号表用来存储程序中各个变量的相关信息,如类型,作用域,访问控制信息,是一种key-value结构,数据结构可以是哈希表(查找快),也可以是红黑树(省空间)

    符号表处理作用域的方法:一是用一张表,进入作用域就插入元素,离开作用域就删除元素,二是用多张表,构成一个栈,进入作用域就插入一张新表,离开就删除栈顶的表

    符号表处理名字空间的方法:引入标签,标号来区别不同类型

  • 汇编代码

    源程序经过了词法分析、语法分析和语义分析,最终生成汇编代码,生成了 *.s* 文件

g++ -S main.cpp -o main.s

三、汇编

将汇编代码转为机器码。

编译阶段得到的汇编代码并不能直接被计算机识别,计算机的元件(寄存器、cpu等)都是由集成电路实现(与非门、或非门),这些电路组成了复杂的逻辑,每个元件由输入引脚和输出引脚组成,通过输入电流与否来得到输出结果,每个引脚通过0或1来表示是否输入电流,0或1按照一定的规则就组成了机器指令。所以要想计算机理解我们的源代码,汇编指令还得翻译成机器指令,汇编指令只是机器指令的一种助记符(只是给机器指令命了名),它是用来帮助人们更好的理解机器指令而产生的,几乎每一条汇编指令对应一条机器指令。

汇编阶段通过汇编器把前面得到的汇编代码翻译成目标文件,生成.obj或.o目标文件,该文件中存放的就是与源代码等效的机器指令,每一个.cpp文件都会对应生成一个.obj或.o文件

g++ -C *.cpp -o *.o

四、链接

目标文件并不能直接执行,还需要经过链接过程,原因是:

某个.cpp文件调用了另外.cpp文件中的函数或者常量等,它们是相互独立的(每个.cpp对应一个.obj文件),为了解决这类问题,必须要将调用者目标文件与被调用者的目标文件链接起来,最终得到可执行程序(.exe或.elf等)

链接一般分为静态链接和动态链接

g++ *.cpp -o *.exe