-
变量的本质是memory cell(内存块)抽象后的表现。
-
变量的六个属性
- Name 名称
- 不是所有变量都有名称 (例:匿名变量,malloc)
- Address 地址
- 同一个变量在程序中的不同执行时间可能存在于不同的地址(例,函数在不同时间被调用,其中的 本地变量就拥有了不同的地址)
- 一个变量(名称)在程序的不同位置可能存在于不同的地址(例 同名的全局变量与本地变量)
- 如果两个变量名都可以用来访问同一个地址,那它们就叫做aliases别名。(比如 C/C++中可以使用 指针,引用,union) Aliases别名不利于程序的可读性和稳定性
- Value 值
- 指的是变量所代表的memory cell(内存块)中的内容
- Type 类型
- 变量的类型决定了这个变量的所有可能取值范围
- Lifetime 生命时间
- Scope 有效作用域
-
Binding的概念
-
binding指的是实体与属性的联系,比如一个变量和它的类型,一个符号和它代表的操作。
-
binding time指的就是 binding 发生的时间 包括了
- 语言的设计时期 例 :星号* 代表乘法
- 语言的实现时期 例:float 浮点数类型在底层实现的方法以及对应的值范围
- 编译时期 compile time 例:编译期一个变量bind了它的类型
- 载入时期 load time 例:全局变量在载入时期bind了它的内存地址
- 链接时期 link time 例:一个库函数的调用在链接时期bind了库函数的代码
- 运行时 run time 例:一个变量在运行时被计算和赋予新的值
-
static binding VS dynamic binding 静态bind与动态bind
- static binding: binding在运行时之前就完成了,并且在程序运行过程中保持不变
- 变量类型需要声明,显示声明和隐式声明
- 隐式声明通常通过预定的命名规则实现也因此可能会造成笔误却被编译器遗漏,例 :perl中@ 首字母代表该变量是数组 ; 另一种隐式声明使用了上下文 context,也被称为类型推断 type inference
- dynamic binding : binding在运行时中会完成或者允许在运行时中改变
- 变量的类型不是通过声明或者命名规则来赋予的,而是在 赋值语句 中根据右值的类型而决定。
- 因此同一个变量在不同时间被赋不同的值,就会拥有不同的类型(都是暂时的)
- 优缺点比较
- dynamic binding 更加灵活,尤其是作为函数参数时(更容易写泛型函数) 。
- 然而dynamic binding不稳定,因为丧失了在赋值语句中的类型检查——错误地将一个string类型 值赋予给一个int 类型的变量时并不会报错,而是将变量改成了string类型。
- dynamic binding 导致语言运行效率低下,首先,类型检查发生在运行时(代码调用了一个变量 的某个方法时,运行时需要检查这个变量是否支持这个方法);其次,运行时需要额外的类型 标记来标注当前变量的当前类型;最后,该变量的空间必须是可变的(因为不同类型占不同空间) (这一点可以通过指针的方法解决)
- 实现
- dynamic binding只能使用解释器(解释型语言),而static binding通常存在于编译型语言, 因为机器代码无法实现动态类型,而静态类型可以编译成高效的机器代码。
- static binding: binding在运行时之前就完成了,并且在程序运行过程中保持不变
-
-
Lifetime 生命时间(也可以说是 storage binding)
-
一个变量的生命时间就是从它被绑定到一个memory cell(内存块)的时间
-
内存分配(Allocation)——从内存池中获取一块内存
-
内存释放(Deallocation)——内存块被回收
-
四种类型:
- 静态(static) 在程序执行前就已经绑定到内存块,并且持续到程序终止 优点: 高效(直接访问 direct addressing),支持记录历史值 (history-sensitive subprogram support)比如 一个累计访问次数的函数 缺点:缺乏灵活性(不支持递归)
- 栈动态(Stack-dynamic) 当声明语句被执行时,在栈中分配一块内存(比如 函数中的本地变量) 优点: 支持递归,节省空间(栈内存反复使用) 缺点: 在栈中内存分配与释放都发生在运行时,降低效率;不支持记录历史值; 访问效率较低(indirect addressing 间接访问——栈base指针+offset)
- 显式堆动态(Explicit heap-dynamic) 使用显式的指令分配和释放堆中的内存 (如C 的 malloc 和 free) 但只能通过指针或者引用获取内存块 优点: 提供了动态堆内存管理 缺点:效率较低,不稳定(野指针,内存泄露等)
- 隐式堆动态(Implicit heap-dynamic) 通过赋值语句分配和释放堆中的内存 主要用在动态类型语言中 如 JavaScript python php 优点:灵活(泛型代码) 缺点:非常低效(所有变量属性都是动态的);难以错误检查(编译期类型检查)
-
Scope 有效作用域 (即这个变量可见的范围)
- 某个单位unit的本地变量指在其范围内声明的变量; 非本地变量指尽管在单位内可见但是 这个变量并没有在该单位内声明。
- 两种类型:
- 静态作用域 Static Scope
- 直接由代码文本决定
- 从变量名找到变量的声明,由内而外,从本地单位开始找,逐渐向 外层范围enclosing scopes延展。
- 同名的变量,外层的会被内层的隐藏
- 声明顺序问题,一部分语言要求只有声明语句之后的代码才对变量可见,还有一些 语言只要变量有声明,其所在单位内所有地方都可以看见(C# , javascript)
- 如果声明地方在最外层,或者用语言指定的方法声明,就能产生全局变量
- 缺点:变量通常能被需要以外的地方访问(限制不足);当程序在多次重构后 越来越多的变量会逐渐变成全局变量
- 动态作用域 Dynamic Scope
- 不由代码文本决定,而是由程序单位(函数)的调用顺序决定
- 从变量名查找其所指的变量,只能顺着函数调用链依次往上找
- 优点:方便,调用函数无需传递大量的参数
- 缺点: 一个函数里的变量会被它调用的所有函数都看到; 无法静态地检查类型; 代码可读性很差
- 静态作用域 Static Scope
-
Referencing Environments 参考环境
- 一个语句的参考环境指的是它能可见的所有变量
- 对于静态作用域,参考环境就是指所有本地变量和它外层的所有变量
- 对于动态作用域,参考环境就是指所有本地变量和调用它的函数(及以上的函数)的变量
-
Named Constants 带名常量
- 指一个变量只绑定值一次(即值在初始化后不可变)比如 const , readonly ,final等
- 主要用于修饰函数的参数,定义常量等
- 优点: 提高程序稳定性;有益于抽象;