读书笔记

Posted by Wengdada on May 8, 2017

把自己看过的一些书的知识点做下记录,算是学习过程中的一些总结吧

《Java书籍推荐》

《编程语言原理 (第10版)》

第五章 名字 绑定和作用域
变量
  • 5.2.2 名字有区分大小写降低了语言的可读性,因为相像的名字实际上表示不同的实体。从这种意义上来说区分大小写违反了语言构造的基本设计原则:看似一样的事物应该具有相同的意义。
  • 5.3 程序变量是对计算机中一个或者一系列存储单元的抽象
  • 变量可以用一个属性六元组来刻画:名字 地址 数值 类型 生存期 作用域
  • 5.3.2 程序中相同的变量可以在不同的时间与不同的地址相关联,如子程序的局部变量。
  • 用别名的时候多个变量可以有同一个地址,c和c++中创建别名的一种常用的方式就是使用联合类型,也可使用指针和引用,不过这种类型的别名是指针类型特性的一种副作用。指针被解引用的时候,就是那个命名变量的别名。
  • 5.3.3 变量的类型决定变量的取值范围,并决定这种类型的值所定义的操作的集合。
静态绑定与动态绑定
  • 5.4 绑定是属性(6元组)和实体之间的关联
  • 5.4.2 静态绑定:变量在使用前,必须绑定一个类型,类型可能通过显式声明隐式声明来说明。注意声明与定义的区别:声明是用来说明类型与其他属性 ,不分配存储空间;定义说明属性并分配存储空间。在C语言中,可以有多次兼容的声明 ,但只能有一次定义。显示声明是程序中的一条说明语句,列出一批变量名并指明这些变量的特定类型,如 int a; float b; 隐式声明则是通过默认约定而不是声明语句将变量与类型关联起来的方式。可以通过命名约定,比如Fortran中的开头字母是I,J,K,L,M,N开头或则是小写字母,隐式声明为Integer类型,否则为Real类型;也可以要求特殊类型的名字以特殊的字符开始,如Perl语言中以$开头的就是标量,@开头是数组,%开头是散列结构;还可以通过使用上下文,如C#中var sum=0; var name="fred"; 变量var声明必须包含初始值,其类型就是变量的类型。

  • 动态绑定:变量的类型既不是由声明语句来说明的,也不能通过名字的拼法来确定,而是在赋值语句给变量赋值的时候变量才与类型绑定。如Javascript中的 list=[10.2,3.5];
  • 总之,判断是静态绑定还是动态绑定,关键是要看变量在使用前是否已经绑定了一个类型,若是在赋值语句给变量赋值的时候才绑定类型,那么就是属于动态绑定,注意,像是C#提到的var sum=0; 在使用之前已经根据上下文判断出var的类型是int型了,像是Javascript中的 list=[10.2,3.5]; 则需要等到赋值语句结束之后才知道list是什么类型的数。
存储绑定与生存期
  • 5.4.3 为什么要了解存储绑定? 命令式程序设计语言的根本特征很大程度上取决于这种语言中变量的存储绑定的方式,变量的生存期是变量绑定在某个存储地址的时间,要分析变量的生存期也要分析存储绑定。为了分析变量的存储绑定,把标量变量的生存期分为4类,静态变量,栈动态变量,显性堆动态变量,隐性堆动态变量。
  • 静态变量:在程序运行前就绑定到存储单元,且在程序结束运行前一直绑定在相同的存储单元上如 C中全局变量、用static修饰的局部变量,Java中类变量、用static修饰的局部变量,Python中的类变量。
    • 优点 - 高效:可以直接寻址,无分配与解除分配所需要的开销
      • 缺点
        • 灵活性低:不支持递归子程序
        • 不能共享存储空间,需要独立分配
  • 栈动态变量:在声明语句确立后才产生存储绑定,类型是静态绑定的,声明语句确立是程序执行到声明所在的代码才发生的,这种确立发生在运行期间。栈动态变量的存储空间是从运行时栈中分配的。如C函数中局部变量,Java函数中基本类型的变量、对象的引用变量。 - 优点 - 支持递归子程序,递归子程序的任何一个活动副本都有自己的局部变量版本,使用栈动态变量,很容易满足这些要求。 - 子程序中的所有局部变量都可以共享相同的存储空间 - 缺点
    - 分配和解除分配需要额外的开销 - 间接寻址导致访问慢

  • 显性堆动态变量:显式使用分配/解除分配指令来指明程序运行过程中何时绑定变量与(分配来的)内存单元。由于显式堆动态变量在编译时和类型绑定,所以是属于静态绑定,这种变量与存储空间的绑定发生在运行时。如C中malloc和free函数,Java的对象都是显式堆动态变量,Java的new和垃圾回收机制 - 优点 - 提供动态存储管理机制,容易构建动态数据结构,比如链表和树结构 - 缺点 - 可靠性差:如指针很难被正确使用 - 效率较低:如引用变量有额外开销,堆的管理问题

  • 隐式堆动态变量:只有在赋值时才绑定到堆动态存储空间。如 list=[10.2,3.5]; - 优点 - 高度灵活性,如编写通用的程序 - 缺点 - 需维护动态属性的开销 - 可靠性差,编译器较难发现错误
作用域
  • 5.5.1 静态作用域 在执行之前就可以静态德决定变量的作用域,通过看代码就可以可以决定程序中每个变量的类型。
  • 静态作用域有两类,一类是子程序可以嵌套,并产生嵌套的静态作用域,如JavaScript,Common LISP,Python等,另外一类是不可以嵌套,入基于C的语言。本节对静态作用域的讨论将集中在允许嵌套的子程序。
  • 在静态作用域中,当发现一个变量的引用,想要确定该变量的属性,必须找到该变量的声明语句。那怎么找呢?假设子程序sub1中对x进行了引用,那么在sub1子程序中进行搜索,没有就在声明子程序sub1的子程序中(sub1的静态父辈)中找,还是没有就找子程序sub1的静态父辈的静态父辈…知道最大的包含子程序–静态祖先code1 如上面的图所示,先在sub2中找,找不到x的声明,找到sub2的静态父辈big,在big中找到。
  • 使用静态作用域的语言中,不管是否允许嵌套,一些变量的声明对其他的代码可以隐藏起来。还是如上的图片,big中的x隐藏在sub1中。而像Ada,祖先作用域中的隐藏变量可以使用选择引用来访问,在sub1中可以通过big.x就可以访问big中的x。在C++中可通过域操作符::来访问内部作用域中隐藏的全局变量。
  • 5.5.4 全局变量 全局作用域 定义在函数外面的变量是全局变量,对文件中的函数是可见的。C的全局变量对文件中所有的后续函数都是隐含可见的,除了函数中有同名的局部变量。对于PHP来说,全局变量并不是在任意函数中都是隐式可见的,若函数中包含一个与全局变量同名的局部变量,此时可以通过$GLOBALS数组来访问,若不包含,则在global中声明包含全局变量,这样全局变量就成为可见的了。 code2 JavaScript的全局非常类似于PHP,但是无法访问与函数中局部变量同名的全局变量。Python中全局变量可以在函数中引用,但是只有在函数中生命为全局变量,才能在函数中赋值。 code3 code4
  • 在函数后面定义的全局变量,要使得它在函数中可见,把它声明为外部的即可。如extern int sum;
动态作用域
  • 基于子程序的调用序列,是基于作用域相互之间时间关系而不是基于作用域相互之间的空间关系,这种作用域只能在运行期间确定。 code1 还是以这个图为例分析,假设big调用sub1,sub1再调用sub2,那么搜索过程将从sub2->sub1->big,但是在sub1中找到了,所以sub2中的引用x是在sub1中声明的x。假设big调用sub2,那么搜索过程将从sub2->big,,所以sub2中的引用x是在big中声明的x。若是静态作用域的话,不管哪种调用顺序,都是引用big中的x。
引用环境
  • 静态作用域中的引用环境是指在它局部作用域中声明的变量和在它的祖先作用域中声明的所有可见变量的集合。 此处输入图片的描述 注意这里的sub1的作用域不是sub3的作用域的静态祖先,因为在sub1中声明的变量是栈动态的,这些变量在sub1没有执行之前不会与存储空间绑定,sub1没有执行之前sub3可能已经执行了,sub3不能访问sub1中的变量,这些变量不一定在sub3执行期间与存储空间绑定。{这里要问下老师}
  • 动态作用域中的引用环境是局部作用域中声明的变量和当前活跃的其他子程序中的所有可见变量的集合。若子程序开始执行且未终止,这个子程序是活跃的。【code6】 此处输入图片的描述
命名常量

c#中的命名常量有两种,一种是const定义,另外一种是用readonly定义。前者与值的绑定发生在编译时,意味着绑定值只能是字面量或者其他的const成员来指定,后者是动态与值绑定的,能够在常量的声明或者静态构造函数中赋值。所以若程序需要在每次使用的时候都有一个相同的值,用const,若需要一个常量值对象且对象的值只有在创建的时候才能确定,而且在程序的不同执行情况下是相互不同的,此时用readonly常量。


面试书籍推荐