第1章 概论

编译器将高级语言翻译为机器语言,是程序执行的基础工具。本章介绍编译器整体结构、各阶段职责及相关程序。

1.1 为什么要用编译器

  • 机器语言 → 数字代码,难编写难维护
  • 汇编语言 → 符号形式,依赖特定机器
  • 编译器 → 将高级语言翻译为机器语言,提高开发效率
  • 历史:FORTRAN(1954-1957, John Backus)首个编译器
  • Chomsky 分类体系:0~3 型文法,其中 2 型(上下文无关文法)对程序设计语言最有用

1.2 与编译器相关的程序

程序 作用
解释程序 立即执行源程序,不生成目标代码(速度慢 10 倍+)
汇编程序 将汇编语言翻译为机器代码
连接程序 将多个目标文件合并为可执行文件
装入程序 处理可重定位地址
预处理器 删除注释、包含文件、宏替换
编辑器 编写源程序(IDE 中的结构编辑器)
调试程序 判定执行错误,支持断点、变量查看
描述器 搜集程序执行统计(调用次数、执行时间)
项目管理程序 管理多文件版本(sccs、rcs)

1.3 翻译步骤(六个阶段)

编译器的工作分为六个阶段,每个阶段将源程序从一种表示转换为另一种表示。辅助部件(符号表、文字表、错误处理器)与各阶段交互。

各阶段详解

  1. 扫描程序(词法分析):字符序列 → 记号(token)

    • 例:a[index] = 4 + 2 → 8 个记号(a, [, index, ], =, 4, +, 2)
  2. 语法分析程序:记号 → 分析树/语法树

    • 分析树:内部节点为非终结符,叶子为终结符
    • 语法树(抽象语法树 AST):浓缩的分析树,去除不必要节点
  3. 语义分析程序:类型检查、声明处理

    • 计算静态语义(编译时确定)
    • 属性(数据类型等)作为注释添加到树中
  4. 源代码优化程序:常量合并、代码改进

    • 例:4 + 26
  5. 代码生成器:中间代码 → 目标代码

    • 目标机器特性成为主要因素
  6. 目标代码优化程序:选择寻址模式、删除多余操作

1.4 编译器中的主要数据结构

数据结构 作用
记号 枚举值表示记号类型(单符号先行)
语法树 基于指针的动态分配结构
符号表 记录标识符信息(函数、变量、常量、类型),通常用杂凑表
常数表 存放常量和字符串,快速插入和查找
中间代码 文本串数组、临时文件或链表
临时文件 用于反填地址等问题

1.5 编译器结构的其他视角

分析和综合

  • 分析:词法分析 + 语法分析 + 语义分析(易懂、数学性强)
  • 综合:代码生成(需专业技术)

前端和后端

  • 前端:依赖源语言(扫描、分析、语义分析)
  • 后端:依赖目标语言(代码生成器)
  • 中间表示作为前后端交流媒介 → 实现可移植性

遍(pass)

  • 一遍编译:速度快但代码效率低(Pascal、C)
  • 多遍编译:优化效果好(典型为 3~8 遍)

1.6 自举与移植

T 型图

T 型图描述编译器之间的关系:用 H 语言编写的编译器,将 S 翻译为 T。

自举(bootstrapping)

  1. 用汇编写一个"虽快但不佳"的编译器
  2. 用这个编译器编译"较好的"编译器
  3. 用较好的编译器重新编译自身 → 最终版本

移植

重写后端生成新机器代码 → 交叉编译 → 在新机器上自举

1.7 TINY 样本语言与编译器

  • TINY:简单的教学语言
    • 无过程、无声明、所有变量为整型
    • 仅有 ifrepeat 控制语句
    • read/write 用于 I/O
  • TINY 编译器:4 遍(扫描分析 → 符号表 → 类型检查 → 代码生成)
  • TM 机:虚拟目标机器,8 个通用寄存器,RISC 风格

1.8 C-Minus

  • C 子集:整型变量、整型数组、函数
  • 局部/全局声明、递归函数、if/while
  • TM 机作为目标代码

考试重点

★★★ 第一梯队

考点 典型题型
编译器各阶段及输入/输出 画图题(图 1-1)
前端 vs 后端 区分标准和意义

★★☆ 第二梯队

考点 典型题型
遍(pass)的概念 与阶段的关系
解释 vs 编译 各自的适用场景
TINY 和 C-Minus 的语言特点 简答题

★☆☆ 第三梯队

考点 典型题型
自举和 T 型图 T 型图的组合与归约
编译器相关程序 各程序的作用区分