运行环境
绑定
Binding的概念
–将符号名和相应目标数据(的地址)对应起来
标识符与数据目标的对应
–变量名──数据存储单元地址
–过程名、函数名──程序段入口地址
相关问题
–变量和过程的作用域,决定绑定的有效期
绑定的时机与策略
语言定义的标识符的生存期决定最终绑定的时机
–全局变量:全程有效——程序装入时
–局部变量:分段有效——进入过程或分程序时
静态(Static)绑定
–编译时指定(相对地址)
–词法分析期间——在符号表中建立变量的表项
–回忆:说明语句的语义分析中的字节数计算,填写变量地址(enter)
动态(Dynamic)绑定
–运行时指定(具体地址/相对地址)
–回忆:动态数组
过程/函数名的绑定
为过程指定程序代码段入口地址
**静态绑定:**编译时指定相对地址
–(词法分析:在符号表中建立过程的表项)
–语义分析:构造目标代码,填写过程的入口地址
–如:函数、子程序
动态绑定
–运行时指定——函数名作为形式参数(formals)
–如:函数指针、虚函数(C++)
存储组织与分配
运行时刻的内存划分
静态存储分配
特点
–编译时刻确定存储位置
–访问效率高
主要用途
–子程序的目标代码段
–全局数据目标(全局变量)
顺序分配算法
–按照程序段出现的先后顺序逐段分配
分层分配算法
允许程序段之间的覆盖(覆盖可能性分析)
静态存储分配无法克服的问题
1动态数组问题
–层次单元法
•层次单元
–进入分程序:将直接外层分程序的层次单元内容植入本层层次单元
•标准单元的使用
–调用语句:将本层层次单元内容送标准单元
–过程说明:将标准单元内容送本层层次单元
2递归调用问题
–栈式存储分配
特点:
–嵌套调用次序 –先进后出 –生存期限于本次调用 –自动释放
用途
–活动记录——栈单元对应一次过程调用所需存储
–过程的局部数据区
3被调用者的生存期超过调用者/局部数据需要保留( save )
–堆式存储分配
•用于动态数据结构
–存储空间的动态分配和释放
•实现方法
–将内存空间分为若干块,根据用户要求分配
–无法满足时,调用无用单元收集程序将被释放的块收集起来重新分配
参数传递
传值调用
–过程调用时计算实参(Actual),将值存到活动记录
–形参(Formal)与活动记录的实参绑定,运行时将形参作为局部变量使用
引用调用
–如果实参(地址)具有左值,则存放其左值到活动记录中;否则计算出表达式的值,将此值存入一个单元,并将该单元的地址传给被调用者
复制恢复
•将参数的左、右值同时传给被调用者,被调用者直接使用右值,并将计算结果按照左值返回给调用者
传名调用
•将被调过程的过程体复制到调用处,并将每一个形参“文字地”替换成实参
•用换名子程序实现Thunk
•是一种早期的语言ALGOL用的一种参数传递方式
过程调用
•过程(procedure)
–子程序(subroutine)、函数(function)
•过程的定义与调用
–形参和实参的结合:参数计算与传递
–调用与返回
工作方式
•调用方:当前环境的保存与恢复
•被调方:构造环境,参数绑定
过程调用实现
简单过程调用
–实在参数的计算和保存
–控制转移、返回地址的保存
–实在参数和形式参数的结合(多种结合方式)
–局部变量的处理
–返回值的处理
递归过程调用与过程参数
–每层过程调用信息的保存与相应信息的查找
活动记录中过程所用信息
静态语义分析
静态语义: 刻画程序在静态一致性或完整性方面的特征;仅当程序通过了静态语义检查,才能完成后续的中间代码生成和目标代码优化。
主要任务
- 类型检查(type checks)
检查每个操作是否遵守语言类型系统的定义 - 名字的作用域(scope)分析
建立名字的定义和使用之间联系 - 控制流检查(flow-of-control checks)
控制流语句必须使控制转移到合法的地方(如 break语句必须有合法的语句包围它) - 唯一性检查(uniqueness checks)
很多场合要求对象只能被定义一次(如枚举类型的元素不能重复出现) - 名字的上下文相关性检查(name-related checks)
某些名字的多次出现之间应该满足一定的上下文相关性
类型检查
类型检查程序(type checker)负责类型检查
• 验证程序的结构是否匹配上下文所期望的类
• 为相关阶段搜集及建立必要的类型信息
• 实现某个类型系统(type system)