《程序语言设计》书中关于作用域有如下介绍:如果标识符x出现在函数体,而x又不是在函数内部定义的,那么x的值必然依赖于函数外部的某个变量.这 种情况下x的存储位置不在函数的活动记录中.因为x在其他某个块中定义,所以为了函数中能访问x,需要在栈里的其他活动记录中找到x.
有两个主要规则用于寻找全局标志符的声明:
1.静态作用域 一个全局标志符表示距离当前作用域最近的外层作用域中声明的同名变量.
2.动态作用域 一个全局标志符表示最近的活动记录中的同名变量.
静态作用域规则寻找一个变量声明时依赖的是源程序中块之间的静态关系(不变的);而动态作用域规则依赖程序执行时(动态的)的函数调用顺序.
访问链被用于维护静态作用域
一个活动记录的访问链(access link)指向源程序中最近的外层块对应的活动记录.内嵌块不需要访问链,因为最近的外层块就是最近进入的块-其活动记录由控制链(control link )指向着.然而对于函数而言,最近的外层块是源程序中函数定义的地方.因为通常函数的控制链和访问链指向不同的活动记录.一些作者将访问链称为静态链 (static link),因为访问链反映的是源程序中块的静态嵌套结构.
另外静态作用域又被称为语句作用域(Lexical scoping),作用域基本的作用是在不同的块间保持变量的独立.
在静态作用域,一个名称总是指向它的本地语句环境.这是程序文本的一个属性其由语言实现来创建且独立于运行时调用栈(call stack),因为它的匹配仅需要分析静态程序文本,所以又称为静态作用域.
动态作用域 Daynamic scoping
在动态作用域,每个标识符有个全局栈的绑定.引入一个名为x的本地变量将一个绑定推入这个全局的x栈(它可能是空的),当控制流(control flow)离开作用域,本地变量被释放(poped off).在任何上下文中演算x总是取决于顶部的绑定.换句话说,全局标识符引用和最近的环境关联的标识符.注意这个不能在编译器执行因为绑定栈只存在于 运行期,这也是这种类型的作用域被称为动态作用域的原因.
通常,一些块被定义用来创建绑定其声明周期是块的执行期;这增加了一些静态作用域特征到动态作用域过程.然而,由于一段代码能被许多不同位置和情景 调用.很难确定当一个变量被使用(或者存在)一开始什么绑定会被应用.这可能是有益的;应用(application)的最少知识原则 (principle of least knowledge)建议代码避免依赖变量值而定.但使用这个值取决于变量的定义.这个共享数据(shared data)的牵强解释能够提供一个非常灵活的系统以适应一个函数行为到系统的当前状态(或策略).然而,这个益处依赖于所以变量都使用这种方式的细致文 档,同时依赖于避免关于变量行为的设想.且不提供任何检测程序不同部分冲突的机制.
动态作用域比较容易实现.找到一个标识符的值,程序能够穿过运行时栈,为标识符检查每个活动的记录(每个函数的栈帧)的值,实际上,通过使用一个关 联列表(association list)会使它更有效率,而列表是一个栈的 名/值 对.无论什么时候声明被创建 名/值 对被推入这个栈,并且无论何时变量离开作用域其被弹出.一个更快一点的替换策略是使用一个中间参考表,其每个名字和它的当前含义关联.这避免了一个在运行 期去查找一个特定名字的线性搜索,但维护这个表更复杂了.注意这两个策略都使用后进先出的顺序来绑定任意的变量;
参考资料: