6.3 非局部名字的访问

本文探讨了编程语言中非局部名字的访问,包括无过程嵌套的静态作用域(如C语言中的全局变量)、有过程嵌套的静态作用域(如Pascal),以及动态作用域的特点和实现。作者通过实例解释了这些作用域规则对程序设计的影响,强调了它们在函数交互和灵活性方面的角色。
摘要由CSDN通过智能技术生成

 

6.3 非局部名字的访问

在编程语言中,处理在当前作用域之外声明的变量或名字的能力是理解作用域规则的核心。这些名字被称为非局部名字。本节将详细探讨在不同作用域规则下,如何访问这些非局部名字。

6.3.1 无过程嵌套的静态作用域

静态作用域,也称为词法作用域,根据程序代码的字面结构来确定名字的作用域。在不支持过程(函数)嵌套的语言中,如C语言,这种作用域规则的实现相对简单直接。

C语言的静态作用域

C语言的设计不允许函数内部再定义函数,这意味着所有的非局部名字引用都必须指向在函数外部定义的全局变量。这些全局变量的作用域从它们声明的位置开始,延伸至文件末尾,除非在某个函数内部有相同名字的局部变量被声明,这时局部变量会隐藏同名的全局变量。

全局变量的存储位置在编译时确定,因此,在任何函数内对这些全局变量的引用都可以直接通过它们的静态地址来实现。这种方法简化了编译器的设计,同时也提高了程序的运行效率,因为访问全局变量的地址不需要在运行时解析。

静态作用域的好处

在无过程嵌套的静态作用域下,程序中的过程(或函数)可以作为参数传递给其他函数,或作为结果返回。这是因为在这种作用域规则下,过程中引用的任何非局部名字都具有全局作用域,其地址在所有过程中都是可用的。这种设计在C语言中表现为函数指针的使用,允许高度的模块化和函数间的灵活交互。

通过探讨C语言中的静态作用域实现,我们可以深入理解作用域规则对程序设计和语言特性的影响。在下一节中,我们将讨论支持过程嵌套的语言如何处理非局部名字的访问,并比较其与无过程嵌套语言的不同之处。

 

 

6.3.2 有过程嵌套的静态作用域

在支持过程嵌套的语言中,静态作用域规则允许在一个过程内部定义另一个过程,形成了一个嵌套的作用域层次。这种结构对于理解如何访问非局部名字至关重要。Pascal语言是支持过程嵌套并采用静态作用域规则的一个典型例子。

Pascal语言中的静态作用域

在Pascal中,如果一个过程p中没有对名字a的声明,那么适用于pa出现的声明位于p的某个外围过程q中。这种情况下,q中对a的声明从静态代码结构上看比任何其他a声明更靠近p中的这个a出现。

为了具体说明,考虑一个将快速排序算法用Pascal语言重写的例子。在这个例子中,partition函数嵌套在quickSort过程内部,并且引入了一个exchange过程。这种过程声明的嵌套结构通过一个阶梯图表示,其中partition函数中的某个变量a的最近的外部声明位于程序的全局作用域中,即使该变量在partition函数内部被引用。

实现有过程嵌套的静态作用域

在实现静态作用域时,引入了“过程嵌套深度”的概念,以及一种称为“访问链”的机制来处理非局部名字的访问。访问链是每个活动记录中的一个指针,它指向当前过程的直接外围过程的活动记录。这种机制允许运行时系统沿着访问链向上查找,以定位非局部变量的声明所在的活动记录。

例如,如果一个过程p的嵌套深度为n_p,并且它引用了一个嵌套深度为n_a的变量an_a <= n_p),那么可以通过追踪访问链n_p - n_a次来找到包含变量a的活动记录。这种查找机制确保了即使在深层嵌套的情况下,也能正确地访问到非局部名字。

访问链的建立

当一个过程被调用时,其活动记录的访问链需要被正确设置以反映嵌套结构。如果被调用的过程嵌套在调用过程之内,则其访问链直接指向调用过程的活动记录。如果嵌套深度大于或等于调用过程,根据静态嵌套关系,通过追踪调用过程的访问链可以找到正确的活动记录,进而建立被调用过程的访问链。

过程作为参数传递

当过程作为参数传递时,其访问链也需要一并传递,以确保在新的调用上下文中仍能正确访问非局部名字。这种机制允许在Pascal等语言中灵活地使用高阶函数,同时保持静态作用域规则的一致性和正确性。

通过这种复杂的机制,支持过程嵌套的静态作用域语言能够处理非常复杂的作用域和命名问题,同时为程序员提供了强大的编程工具。这种作用域规则在程序设计中提供了额外的灵活性,允许更紧密地封装功能和数据,促进了模块化和重用。

 

 

*6.3.3 动态作用域

动态作用域是一种与静态作用域截然不同的名字解析规则。在动态作用域下,名字的绑定取决于程序的调用顺序而非其在代码中的位置。这意味着一个变量的值由最近的活动调用上下文决定。

动态作用域的特性

在动态作用域规则下,当一个过程q被另一个过程p调用时,q中的非局部名字a会绑定到p中对应名字a的存储单元。这种绑定方式确保了q在执行时,对a的引用与p中对a的绑定是一致的。换句话说,qa的访问实际上访问的是pa的值。

动态作用域的这一特性导致了其与静态作用域的主要区别:名字的作用域是在运行时动态决定的,而非在编译时静态确定。这种机制允许函数根据当前的调用栈来解析非局部变量,从而能够访问到最近调用过程中的变量。

动态作用域的实例

考虑一个简单的示例程序,该程序展示了动态作用域如何影响变量的解析。假设有两个过程showsmall,其中show用于输出变量r的值,而small则设置r的值并调用show。根据动态作用域的规则,show输出的r值将取决于调用showr的最近一次绑定。

在静态作用域下,变量r的值总是由它在代码中的声明决定,因此无论如何调用show,输出的都是同一个r的值。而在动态作用域下,show的输出会根据最近的r值变化而变化,即显示调用showr的当前值。

动态作用域的实现

动态作用域可以通过两种主要方式实现:

  1. 深访问(Deep Access):这种方法通过搜索运行时的调用栈来实现动态作用域。搜索从当前活动记录开始,沿着调用链向上查找,直到找到包含所需非局部名字的第一个活动记录。

  2. 浅访问(Shallow Access):在这种方法中,程序中的每个名字都在静态数据区分配一个空间,用于保存其当前值。当一个新的过程被调用时,它的局部变量使用这些预分配的存储单元。过程结束时,原先的值会被恢复。

比较深访问和浅访问
  • 深访问优点是它直接映射了程序的调用结构,但可能需要较长的时间来访问非局部名字,因为需要遍历调用栈。
  • 浅访问则提供了更快的非局部名字访问时间,但在过程的入口和出口处需要额外的工作来保存和恢复变量值。

动态作用域提供了一种强大的机制,允许函数根据其被调用的上下文动态解析非局部变量。这种灵活性在某些编程场景中非常有用,尤其是在需要根据调用历史动态改变行为的情况下。然而,它也可能导致代码难以理解和维护,因为变量的作用域不再是由其在代码中的位置静态决定的。

 

 

 

 

 

 

 

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值