语言的设计之名字(一)
早期推动语言发展的是两个互为补充的目标,一个是机器无关性,另一个是更易于进行程序设计。近年来,在推动语言进步方面,机器无关性并没有起到很大的作用。另一方面,使程序设计更易于设计是一个更难以琢磨也更具挑战性的目标,它影响着语言设计的各个方面,从历史上看与其说是一个科学问题,不如说更多的是审美以及实验和纠正错误方面的问题。
程序语言设计的核心问题包括:名字、控制流、类型子、程序、类五个方面。名字使我们可以使用符号形式的标识符来代表标量、常量、操作和类型等,而不是使用更低级例如地址一类的概念。这个过程可以称之为词语抽象。抽象是一个过程,程序员通过抽象将一个名字与一段可能很复杂的程序片段相关联,使我们可以将它看成一种用途或者功能,而不必关心这种功能是怎样获得的。抽象通过隐藏许多不相关的细节,减少了概念的复杂性,从而使程序员在任何特定时刻可以将注意力集中到程序正文的一个易于管理的子集上。子程序是控制抽象,将复杂代码隐藏到一个简单接口之后。类是数据抽象,使程序员可以把数据表示的细节隐藏到一组(相对)简单的操作之后。
几个与时间相关的概念:语言设计时、语言实现时、编写程序时、编译时、连接时、装入时、运行时。大多数现代操作系统都对虚拟地址和物理地址进行区分,虚拟地址在连接时选择,物理地址实际上在运行中还可以改变。术语静态与动态分别指在运行之前建立的约束和在运行时建立的约束。
1.对象生存期和存储管理
在讨论命名和约束时,重要的是区分名字和他们所引用的对象。几个关键时间:对象的创建,约束的创建,对变量、子程序、类型等的引用,对可能暂时无法使用的约束进行失活或重新激活,约束的撤销,对象的撤销。
对象的生存期通常对应于三种主要的存储分配机制:1)静态对象被赋予一个绝对地址,这个地址在程序整个执行过程中都保持不变;2)栈对象按照先进后出的方式分配和释放,通常与子程序的调用和退出同时进行;3)堆对象可以在任意时刻分配或者释放。
1.1静态分配
全局变量、局部静态变量、数值或者字符串值是静态分配的。此外,大多数编译器会生成各种各样的表格用于运行时各种支持例程。那些在程序执行时不应该被改变的静态分配对象,通常都分配到一个受保护的制度存储区中,这样任何试图重写它们的企图都将导致处理器中断,使操作系统可以发出运行时错误信息。
从逻辑上讲,局部变量是它们的子程序被调用时创建,并在其子程序返回时撤销。不过具体的语言实现并不一定在运行时来执行对应于这些变量的创建和撤销工作。例如早期的Fortan并不支持递归,因此在任意时刻都不会同时出现对同一子程序的多个处于活动状体的调用,这样编译器就可以对局部变量使用静态分配方式,有效的安排不同调用中各个变量共享同一位置,避免了运行中创建和撤销工作所带来的那些开销。
许多语言要求命名常量拥有一个在编译时就可以确定的值。这种命名常量以及常量文字量,有时被称为明确常量或编译时常量。在其它语言(C和Ada)中,常量就是在加工之后不能再改变的变量。他的值虽然保持不变,但有时其依赖的值只有到运行时才能知道。
参数和返回值:如果可能,编译器总是将它们保存在寄存器中,但有时也会用到内存空间,例如参数个数太多的时候,寄存器个数不足以存储参数。
临时量:通常是在复杂计算中产生的中间值。好的编译器会尽可能的将其存储在寄存器中。
1.2基于栈的分配
如果一种语言允许递归,局部变量就不能使用静态分配方式。幸运的是,子程序调用所固有的嵌套性质,我们可以很容易的在栈上为局部变量分配空间。在运行中,一个子程序的每个实例在栈中都有自己的帧,也称活动记录。传递给下一个例程的参数放在帧的顶部,方便被调用方方便的找到它们,其余信息(临时量、局部变量、返回地址、簿记信息等)根据语言、机器、编译器的不同而不同。将局部变量进行栈分配优于静态分配方式(即使不支持递归的语言中),因为当前活动的子程序需要的空间一般是小于所有子程序所需要的空间,从而节省空间。
1.3基于堆的分配
堆是一块存储区域,其子存储块可以在任意时刻分配和释放。简单的说是有程序员自己申请和释放的空间。堆空间管理有许多策略,面临的问题主要是内部碎片和外部碎片。最佳适配算法 最先适配算法。
1.4 废料收集
2 作用域规则
作用域:一个约束起作用的正文区域。在大多数现代程序设计语言中,约束的作用域都是静态确定的,也就是在程序编译时确定的(c、c++)。其他一些语言(APL、Snobol等)则是动态作用域:它们的约束依赖于运行时的执行流。除了讨论“约束的作用域”之外,有时我们还会用到术语“作用域”本身。典型的作用域如模块、子程序、类、结构化控制语句的主体,有时被称为块。
在程序执行中的任意给定点,所有处于活动状态的约束的集合称为当前引用环境。
2.1静态作用域
从语义上讲,局部变量的生存周期就是所在的子程序的一次执行。在C语言中,程序员可以显示的用static关键字覆盖这一规则,使得局部变量生存期贯穿程序的整个执行期。
2.2嵌套子程序
作用域是嵌套的,内层声明的变量,在外层不可见,在其内层可见。如果其内层有同名变量则会屏蔽该变量。
访问非局部对象:找到外围作用域的最简单方法就是在每个帧中维护一个静态链接(指向相应的“父帧”)。
2.3声明的顺序
一些语言局部变量的作用域是声明的位置到块的结束(除空洞外),另外一些语言局部变量的作用域就是整个块(空洞除外),这意味着可以在变量声明前使用该变量。
2.4声明和定义
在要求使用某个名字之前必须先对其进行声明的语言中,递归的类型和子程序会带来一个问题:两个声明如何才能出现在彼此的前面?c和c++采用区分对象的声明和定义来处理这一问题。

浙公网安备 33010602011771号