基本概念解释

在程序设计语言中,变量可以分为自由变量(free variable)与约束变量(bound variable)两种。简单来说,一个函数里局部变量和参数都被认为是约束变量;而不是约束变量的则是自由变量。
在冯·诺依曼(von Neumann)计算机体系结构的影响下,命令式语言(imperative language)里一个变量的属性可以看作一个六元组:(名字,地址,值,类型,生命期,作用域)
名字:顾名思义。
地址:变量所关联的存储器地址。这个属性有明显的冯·诺依曼体系结构的色彩。
:变量所关联的存储器单元的内容。
类型:规定了变量可以取的值得范围,以及该类型的值可以进行的操作。根据类型的值的可赋值状况,可以把类型分为三类:
- 1、一级的(first class)。该等级类型的值可以传给子程序作为参数,可以从子程序里返回,可以赋给变量。大多数程序设计语言里,整型、字符类型等简单类型都是一级的。
- 2、二级的(second class)。该等级类型的值可以传给子程序作为参数,但是不能从子程序里返回,也不能赋给变量。
- 3、三级的(third class)。该等级类型的值连作为参数传递也不行。
生命期:变量与一个特定的存储区地址相绑定的过程。一个变量的生命期从它与一个特定的存储器地址相绑定开始,到它与存储器解除绑定为止。根据生命期分类,变量可以被分为四类:
- 1、静态(static)。该种变量的存储器分配在程序开始运行之前就决定,并且在程序运行过程中一直不变,直到程序结束为止。
- 2、栈动态(stack-dynamic)。该种变量的存储器分配在声明它的语句被执行到的时候才决定,但变量类型是静态决定的。顾名思义,空间是在运行时栈上分配的。
- 3、显式堆动态(explicit heap-dynamic)。该种变量由程序员显式使用运行时指令(或运算符)来指定在堆上空间的分配(和/或回收)。典型的例子是使用newdelete运算符。
- 4、隐式堆动态(implicit heap-dynamic)。该种变量每次被赋值时都会重新在堆上分配空间,不需要特别的运行时指令(或运算符)来指定。
作用域:变量在语句中可见的范围。如果在某个语句中可以引用某个变量,则该变量在该语句中可见。根据作用域的特征分类,程序设计语言中所支持的作用域可以被分为:
- 1、静态作用域。在静态作用域的规定下,变量的作用域与其在代码中所处的位置相关;因为代码可以静态决定(运行前就可以决定),所以变量的作用域也可以被静态决定,所以这种规定被称为静态作用域。
- 2、动态作用域。相对的,在动态作用域的规定下,变量的作用域与代码的执行顺序相关;执行顺序只有在程序运行时才能被决定,所以这种规定被称为动态作用域。
某种意义上来说,这两种规定是统一的:静态作用域由空间(代码字面上的空间)范围决定,动态作用域由时间(程序的执行)顺序决定。但由于作用域本身是个空间概念,所以一般而言静态作用域更容易被人理解。

变量的生命期与作用域并不一样,一个是时间概念,一个是空间(源代码字面上的)概念。但支持静态作用域,并且支持栈动态变量的程序设计语言中,这两个概念会有一些联系,例如:一种支持静态作用域与栈动态变量的语言(例如C)中,一个原始类型(或者在某些语言中,值类型)的局部变量会在栈上分配空间,它的生命期从其所在的函数被调用的时候开始,到调用结束的时候为止;如此生命期就与作用域联系在了一起。
在允许在全局层次定义变量的语言里,全局变量是个特例。全局变量的存储器空间分配与局部变量不一样,一般是静态决定其分配的地址(在存储器的全局空间部分),所以它的生命期涵盖程序的整个执行过程。因此,虽然对于所有函数来说全局变量都是自由变量,但下面的讨论中我们不需要对其做讨论(因为它不涉及动态的存储器空间分配)。某些语言里的static关键字也可以对局部变量指定使用静态存储器空间分配,这样它们虽然仍遵守静态作用域,生命期却与一般的栈动态局部变量不同。

在静态作用域中,也可以分为两类:可以嵌套定义子程序的与不可以的。基于C的语言(指标准C、C++、Java)都无法在一个函数里再嵌套定义函数;而其它的一些,像是Scheme、Pascal、Ada、JavaScript、C#等则允许嵌套的函数定义。
虽然基于C的语言不允许嵌套定义函数,但这类语言都支持块结构。通过代码块,我们可以在一个静态作用域里新建一个嵌套的静态作用域。这样就可以有效的控制变量的作用域,使其尽量的小,便于减少变量名冲突的问题。