编译原理 龙书 第一章 引论

1.1 语言处理器

有编译器和解释器 , 编译器是翻译文本 , 而解释器是同声传译
Java既有编译又有解释的部分
首先它被编译为字节码 , 然后通过用一个虚拟机对字节码加以解释执行

Preprocessor : 预处理器 , 负责将源代码聚合在一起
Assembler : 编译器 , 将预处理过的代码作为输入传递给编译器 , 从而产生一个汇编语言程序作为输出

linker : 链接器 , 由于一个文件中的代码可能指向另一个文件中的位置 ,因此需要链接器解决外部内存地址的问题
loader : 加载器 , 将所有可执行文件放到内存中执行

1.2 编译器的结构

两部分 , 分析综合 , 前端和后端

1.2.1 词法分析

输入 : 源程序组成的字符流
输出 : token序列 , <token-name,attribute-value>

1.2.2 语法分析

输入 : token序列
输出 : 语法树 , 树中每个内部节点都是一个运算 , 子节点表示该运算的分量 , 即一个Token串

1.2.3 语义分析

输入 : 语法树 + 符号表
输出 : 将类型信息存放在语法树或符号表中
一个重要的部分是类型检查(type checking) , 编译器检查每个运算符是否具有匹配的运算分量
如$ a[idx]$ , 则\(idx\)不能是一个浮点数

可能允许某种类型转换 , 即自动类型转换

1.2.4 中间代码生成

输入 : ?
输出 : 中间代码 ,如三地址代码 , 语法树等

1.2.5 代码优化

即中间代码的优化 , 以语义块为单位进行 , 避免复杂运算和重复运算

1.2.6 代码生成

以源程序的中间表示形式为输入 , 把它映射到目标语言 , 形成能执行的极其指令序列

1.2.7 符号表管理

记录源程序中的变量名 , 存储位置 , 作用域 , 参数 , 类型等等

1.2.8 将多个步骤合并为趟

趟(pass) : 每趟读入一个输入文件并产生一个输出文件 , 前端的词法分析 , 语法分析 , 语义分析 , 中间代码生成可以被组合为一趟

1.2.9 编译器构造工具

  • 语法分析的生成器 : 可以根据一个程序设计语言的语法描述自动生成语法分析器
  • 扫描器的生成器 : 可以根据一个语言的语法单元的正则表达式描述生成词法分析器
  • 语法制导的翻译引擎 : 可以生成一组用于遍历分析树并生成中间代码的例程 ?
  • 代码生成器的生成器 : 依据一组关于如何把中间语言的每个运算翻译成目标机上的机器语言的规则 , 生成一个代码生成器

1.3 程序设计语言的发展历程

1.3.1 走向高级程序设计语言

一种分类方式是根据代
第一代语言 : 机器语言
第二代语言 : 汇编语言
第三代语言 : Fortran,C,C++等高级程序设计语言
第四代语言 : 用于特定应用设计的语言 , 如用于生成报告的NOMAD , SQL , Postscript等
第五代语言 : 基于逻辑和约束的语言 , 如Prolog , OPS5等
另一种分类方式是:
强制(命令)式语言 : 指明如何完成一个计算任务 , 如C,C++,Java
声明式语言 : 如Prolog , ML , Haskell,SQL等 , 指明程序中要进行哪些计算的语言
声明式语言短小精湛

冯诺依曼语言 : 指冯诺依曼计算机体系结构为计算模型的程序设计语言

面向对象语言 : 即Java,Python,C++等

脚本语言 : 具有高层次运算符的解释型语言 , 写的程序短小精悍 , 如JavaScript, php, Ruby, python

1.4 构建一个编译器的相关科学

1.4.1 编译器设计和实现中的建模

第三章的正则表达式和有穷状态自动机 , 上下文无关文法用于描述程序设计语言的语法结构等

1.4.2 代码优化的科学

第九章开始 , 相关图 , 矩阵和线性规划的模型

1.5 编译技术的应用

1.5.1 高级程序设计语言的实现

随着面向对象等程序语言的发展 , 编译器相关的理论也不断成长 , 如降低垃圾回收的开销 , 动态编译 , 加速虚拟方法分发的优化技术等

1.5.2 针对计算机体系结构的优化

并行性内存层次结构
内存层次结构由几层不同速度和大小的存储器组成 , 最近的速度快但是容量小 , 合理安排可以提高机器的潜在性能

1.5.3 软件生产率工具

即一些对系统中错误进行定位的技术

类型检查,边界检查内存管理工具
其中 , 内存管理工具可以防止内存泄漏等(C,C++的主要错误来源)

1.6 程序设计语言基础

讲述一些研究过程中出现的术语

1.6.1 静态和动态的区别

如果一个语言使用的策略支持编译器静态决定某个问题 , 那么为静态策略(static policy) , 或者说该问题可以在编译时刻决定

如果一个决策只能在运行程序时被决定 , 那么就是动态策略(dynamic policy)

声明的作用域 : x的一个声明的作用域(scope)是指程序的一个区域(如一个函数内部 , 或者一个for循环内部) , 在其中如果我使用x , 我就是使用它

1.6.2 环境与状态

环境(environment) : 一个从名字到存储位置的映射
状态(state) : 一个从内存位置到它们的值的映射

啥意思呢 ? 我的理解是环境是根据identifier找内存 , 状态是根据内存找值

如 :

int i;
void f(){
  int i;
  i = 3;
}
x = i + 1;

那么使用f中的i时 , i指向了f内部的i的内存 , 且i的所有使用(i=3)都是局部的i
局部的i在栈内存储

1. 名字到位置的静态绑定与动态绑定

大部分从名字到位置的绑定是动态的 , 即需要根据当前作用域判断使用哪个"i"

2. 从位置到值得静态绑定与动态绑定

一般来说 , 位置到值得绑定也是动态的 , 因为位置对应的值会变 , 当然也有例外 , 如#define inf 0x3f3f3f3f

1.6.3 静态作用域与块结构

考虑块结构语言的静态作用域规则
C语言的静态作用域策略概括如下 :
山为作用域 , 人为变量 , 山一层一层 , 人在山上哪层都可以有 , 喊人的时候从山上往下喊第一个

块结构 : 即C中{}包括起来的一部分 , 可以嵌套

1.6.4 显式访问控制

显式访问控制是什么 ?
访问类和结果中的新作用域 , 即public , private , protected等

1.6.5 动态作用域

如果一个作用域策略依赖于一个或多个只有在程序运行时才能知道的因素 , 那么它是动态的

1.6.6 参数传递机制

值调用 , 引用调用 , 名调用(已经不再使用)等

传指针应该可以看着引用调用的方式

1.6.7 别名

别名 , 即两个变量名指向了同一块内存 , 出现在引用或者类似的方法中

posted @ 2025-06-03 22:10  Guaninf  阅读(24)  评论(0)    收藏  举报