程序编译过程_y.z.t的博客-爱代码爱编程
程序编译过程
version : v1.0 「2022.7.28」 最后补充
author: Y.Z.T.
简介: 简单记录程序的编译过程
⭐️ 目录
文章目录
1️⃣ 编译流程
程序的整个编译流程大致分成几个阶段:
- 预处理 : 将预处理指令进行处理 , 预处理器将源文件(.c) 经过预处理变成 文件(.i )
- 编译 : 编译器调用解析工具 , 将预处理后的源文件( .i )编译成汇编文件( .s)
- 汇编 : 这是也是编译的第二阶段 , 通过汇编器将汇编文件( .s) 汇编成可重定位的目标文件( .o)
- 链接 : 将各个目标文件( .o)链接成可执行文件( 也是可执行文件的一种 )
程序编译 , 链接流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SbSXbHIx-1664787594242)(https://pic1.imgdb.cn/item/633aa48716f2c2beb1e98a0d.png)]
2️⃣ 可执行文件
一个可执行文件通常由一系列不同的段(section)
构成:代码段、数据段、BSS段、只读数据段等。
C语言到可执行文件 :
- 函数翻译成二进制指令放在代码段中
- 初始化的全局变量和静态局部变量放在数据段中(.data)
- 未初始化的全局变量和静态变量放在BSS段中(.bss)
- 程序中定义的一些字符串 , printf函数打印的字符串常量放在**只读数据段( .rodata)**中
3️⃣ 预处理
预处理过程就是 在编译源程序之前 , 先处理源文件中的各种预处理指令
预处理主要包括以下操作 :
- 头文件展开: 将
#include
包含的头文件内容展开到当前位置 , 并删除#include
- 宏展开: 展开所有的宏定义,并删除
#define
。 - 条件编译: 根据宏定义条件,选择要参与编译的分支代码,其余的分支丢弃。
- 删除注释。
- 添加行号和文件名标识: 编译过程中根据需要可以显示这些信息。
- 保留
#pragma
命令: 该命令会在程序编译时指示编译器执行一些特定行为。
4️⃣ 编译
汇编过程主要包括以下步骤 :
-
词法分析
-
语法分析
-
语义分析
-
中间代码生成
-
汇编代码生成
-
目标代码生成
4.1 词法分析
词法分析主要用来解析C程序语句 , 词法分析一般会通过词法扫描器从左到右 , 将源程序分解为一系列不能再分解的记号单元–token。
常见token
- C语言的各种关键字:
int
,float
、for
,while
、break
等。 - 用户定义的各种标识符: 函数名、变量名、标号等。
- 字面量: 数字、字符串等。
- 运算符: C语言标准定义的40多个运算符。
- 分隔符: 程序结束符分号、for循环中的等
示例:
sum = a + b / c;
如上所示:
- 经过词法分析后 分解成
sum
,=
,a
,+
,b
,/
,c
,;
八个token
- 如果程序出现中文符号、圆角\半角字符 等 ,程序就会在这个阶段发错编译错误
4.2 语法分析
语法分析主要是对前一阶段产生的
token
序列进行解析, 看是否能构建成一个语法上正确的语法短语(程序、语句、表达式等)。
说明:
- 词法分析语法分析工具在对
token
序列分析过程中, 如果发现不能构建语法上正确的语句或表达式,就会报语法错误:syntax error
- 如果程序语句后 少了结束分号或 循环中少了分号 ,就会在此阶段产生编译错误
4.3 语义分析
语义分析主要对语法分析输出的各种表达式、语句进行检查,看看有没有错误。
例如 :
- 传递给函数的实参与函数声明的形参类型不匹配,
- 使用了一个未声明的变量
- 除数为零了;
break
在循环语句或switch
语句之外出现了,- 在循环语句之外发现了
continue
语句等
4.4 生成中间代码
说明:
- 中间代码是一维线性结构 , 类似伪代码
- 通过中间代码 , 可以很容易的将中间代码翻译成汇编代码
示例:
int main(void)
{
int sum = 0;
int a = 2;
int b = 1;
int c = 1;
sum = a + b / c;
return 0;
}
转换为中间代码三地址码:
main ()
{
int D.4227;
int D.4228;
{
int sum;
int a;
int b;
int c;
sum = 0;
a= 2;
b= 1;
C= 1;
D.4227 = b / c;
sum =D.4227 + a;
D.4228 = 9;
return D.4228;
}
D.4228 = 0;
return D.4228;
}
中间代码转换为 汇编代码:
MOV R0, #2
MOV R1, #1
MOV R2, #1
DIV R3, R1, R2 ; R3 = R1 / R1
ADD R0 RO.R3 ; R0 = R0 + R3
- 将变量变量a、b、c分别放到寄存器R0、R1、R2中,
- 临时变量
D.4427
使用R3代替,然后使用ADD
命令完成累加。
4.5 汇编过程
- 汇编器主要是 将汇编代码翻译成对应的二进制指令;
- 同时生成一些必要的信息 , 以section的形式组装到目标文件中
汇编过程:
5️⃣ 链接过程
- 编译器在编译一个项目时,是以C源文件为单位进行编译的,每一个源文件编译生成一个对应的目标文件(.o)
- 但这些单独的目标文件(.o)是不可执行的 , 属于可重定位的目标文件;
- 它们要经过链接器重定位、链接之后,才能组装成一个可执行的目标文件a.out。
- 链接器将各个目标文件组装在一起后, 重新修改各个目标文件中的变量或函数的地址,这个过程一般称为重定位。
- 链接过程中 , 将各个目标文件分段组装 ; 例如 : 将各个目标文件的代码段放在一起,作为最终生成的可执行文件的代码段; 将各个目标文件的数据段放在一起,作为可执行文件的数据段。