程序编译过程

程序编译过程


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语言到可执行文件 :

image-20220928114314496

  • 函数翻译成二进制指令放在代码段
  • 初始化的全局变量静态局部变量放在数据段中(.data)
  • 未初始化的全局变量静态变量放在BSS段中(.bss)
  • 程序中定义的一些字符串 , printf函数打印的字符串常量放在**只读数据段( .rodata)**中


3️⃣ 预处理

预处理过程就是 在编译源程序之前 , 先处理源文件中的各种预处理指令


预处理主要包括以下操作 :

  • 头文件展开:#include包含的头文件内容展开到当前位置 , 并删除#include
  • 宏展开: 展开所有的宏定义,并删除#define
  • 条件编译: 根据宏定义条件,选择要参与编译的分支代码,其余的分支丢弃。
  • 删除注释
  • 添加行号和文件名标识: 编译过程中根据需要可以显示这些信息。
  • 保留#pragma命令: 该命令会在程序编译时指示编译器执行一些特定行为。


4️⃣ 编译

汇编过程主要包括以下步骤 :

  • 词法分析

  • 语法分析

  • 语义分析

  • 中间代码生成

  • 汇编代码生成

  • 目标代码生成



4.1 词法分析

词法分析主要用来解析C程序语句 , 词法分析一般会通过词法扫描器从左到右 , 将源程序分解为一系列不能再分解的记号单元–token。


常见token

  • C语言的各种关键字: int,floatfor,whilebreak等。
  • 用户定义的各种标识符: 函数名变量名标号等。
  • 字面量: 数字字符串等。
  • 运算符: 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的形式组装到目标文件中

汇编过程:

image-20220928233023855



5️⃣ 链接过程

  • 编译器在编译一个项目时,是以C源文件为单位进行编译的,每一个源文件编译生成一个对应的目标文件(.o)
  • 但这些单独的目标文件(.o)是不可执行的 , 属于可重定位的目标文件;
  • 它们要经过链接器重定位链接之后,才能组装成一个可执行的目标文件a.out。
  • 链接器将各个目标文件组装在一起后, 重新修改各个目标文件中的变量或函数的地址,这个过程一般称为重定位
  • 链接过程中 , 将各个目标文件分段组装 ; 例如 : 将各个目标文件的代码段放在一起,作为最终生成的可执行文件的代码段; 将各个目标文件的数据段放在一起,作为可执行文件的数据段。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值