编程语言的类型系统

大一什么都不懂的时候写的东西,mark而已

1.引言

类型系统被采用并被作为类型检查的一种手段,从二十世纪五十年代的FORTRAN语言编译器就已开始。采用类型论(type theory)观点的编程语言类型系统的研究,在软件工程、编程语言设计、高性能编译器和网络安全等方面都有重要应用.通过学习计算机科学导论这门课程以及课下查阅资料,作者加深了对编程语言中的类型系统的概念的理解,并对其作用和存在的问题有了一定了解。现将所得整理在此读书报告中。
S

2.类型系统(type system)

2.1定义

类型,通常认为是一组可能的值的集合。比如整型,其可能的值是整数的集合;深究系统一词的内涵具有一定的困难。在我们讨论的编程语言这一领域内,当提到“类型系统”时,一个较合理的解释为:一组基本类型构成的“基本类型集合”;及“基本类型集合”上定义的一系列组合、运算、转换方法。
我们可以得到如下的类型系统的定义:
编程语言的组成部分,它由一组定型规则(typing rule)构成,这组规则用来给各种程序构造(变量、表达式和函数等)指派类型
例1: “若M和N都是long类型的表达式, 则M+N也是long类型的表达式”是非形式描述的定型规则

例2:若函数f的某个形参是long类型,则对应的实参也应是long类型。若对应实参是char、short和int类型,则系统会自动把它们提升为long类型。[1]

关于类型系统的定义亦可参见:

A type system is a collection of rules that assign a property called type to various constructs a computer program consists of, such as variables, expressions, functions or modules.[2]

2.2作用

我们知道计算机存储是以二进制方式进行的,并以连续的八个二进制位为一个基本单元——“字节”。以此来看,计算机存储方式是通用的,存储文字、图像、声音或其他别的媒介都没有内在的本质差异。
而实际情况是,我们在编程语言概念上人为引入一系列的迥异的“基本类型”,如C语言中的int和double类型。某种意义上,int和double本身并没有什么差别,都只不过是若干个字节构成的存储单元而已。
那我们为何要设置“复杂”的类型系统?其实就“存储”概念而言,我们用二进制方式,以字节为单位来实现信息的存储,已经足够。但是我们注意到,计算机或者进一步说,编程语言,其作用主要有二,即存储信息和处理信息。事实证明,研究类型理论(type theory),划分类型系统,将在对信息处理方面带来极大便利。

A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute.[3]

The fundamental purpose of a type system is to prevent the occurrence of execution errors during the running of a program.[4]

换句话说,设计类型系统的根本目的是利用类型检查(将在下文中介绍)的方式来保证合法程序运行时侯的行为是良好的。
类型化的编程语言除了有助于进行类型检查,较早的发现错误,还另程序模块可以互相独立地编译,并且可得到更有效的空间安排与访问方式。我们也看到由于不同类型数据运算规则的差异,类型系统的存在也是必要的。

并且,结合类型的实际意义,可知,“类型理论解决了使程序具有意义的基本问题,同时引发了一个问题,即一个有意义的程序没有相应的类型与之对应,这也导致了对更加丰富的类型系统的需求。” [5]

2.3相关概念

我们先引入一些下文论述中会出现的概念
trapped errors导致程序终止执行的错误,如除0,Java中数组越界访问
untrapped errors 出错后继续执行,但可能出现任意行为。如C里的缓冲区溢出、Jump到错误地址
Forbidden Behaviors 语言设计时,可以定义一组forbidden behaviors. 它必须包括所有untrapped errors, 但可能包含trapped errors.
Well/ill behaved: 如果程序执行不可能出现forbidden behaviors, 则为well behaved,否则为ill behaved。
了解了以上概念,我们就可以对编程语言进行区分。
1 一种语言的所有程序都是well behaved——即不可能出现forbidden behaviors,则该语言为强类型(strongly typed)。否则为弱类型(weakly typed),比如C语言的缓冲区溢出,属于trapped errors,即属于forbidden behaviors。
2静态类型(statically typed): 在编译时拒绝ill behaved的程序。
动态类型(dynamiclly typed): 在运行时拒绝ill behaviors, 
3.如果类型是语言语法的一部分,则是显式类型(explicitly typed);如果类型通过编译时推导,是隐式类型(implicity typed), 比如ML和Haskell4.

由此我们可以对常见的编程语言进行分类,分类如下

无类型: 汇编
弱类型、静态类型 : C/C++
弱类型、动态类型检查: Perl/PHP
强类型、静态类型检查 :Java/C#
强类型、动态类型检查 :Python, Scheme
静态显式类型 :Java/C
静态隐式类型 :Ocaml, Haskell

4.类型系统的形式化

类型表达式,如:
int, intint, pointer(int)
定型规则,如:

x:int|-x+1:int

其中|-左侧的被称为定型环境 定型断言:

 |– M : int    |– N : int
 |– M + N : int 

其中形如

a < b    b < c
a < c

的式子我们称之为推理规则。
根据个人理解,定型断言可以看作建立在定型规则之上的一种推理规则。

3.类型检查(Type checking)

编译器对程序进行的检查包括语法检查、类型名变量名及函数名等先声明后引用的检查、类型检查以及其它检查。
我们在此讨论的为类型检查,其实现依赖类型系统。如,若a是long类型的数组,m是long类型,则编译器会发现m + “123”和a + 3.5都有类型错误。
关于类型系统,此处借用来自Wikipedia的定义如下[6]:

The process of verifying and enforcing the constraints of types—type checking—may occur either at compile-time (a static check) or at run-time.

其中提到的两种检查方式为:
Static type checking is the process of verifying the type safety of a program based on analysis of a program’s text (source code).
具体而言,Static type checking不运行被测程序本身,仅通过分析或检查源代码的语法、结构、过程、接口等来检查程序的正确性。静态方法通过程序静态特性的分析,找出欠缺和可疑之处,例如不匹配的参数、不适当的循环嵌套和分支嵌套、不允许的递归、未使用过的变量、空指针的引用和可疑的计算等
Dynamic type checking is the process of verifying the type safety of a program at runtime.
大部分类型安全(type-safe)的语言包含有一些形式的动态类型检测,即使他们同时具有a static type checker,原因在于,许多有用的动能对于静态检查实行起来非常困难。例如,假设有一个程序定义了两个类型A和B,并B是A的一个子类型。此时如果我们要将一个变量从A类型转换为B类型,即对此变量进行向下类型转换,此操作仅在此变量原来即为B类型时才是合法的。而,变量名指向的地址存储的内容只有在程序运行时才可以确定,因此,动态类型检测在判断此类操作是否安全时是必须的。
由此引申第三种类型——Combining static and dynamic type checking。即存在一些编程语言同时允许以上两种类型的检查。

4.c语言的类型缺陷

C语言为笔者唯一较为熟悉的语言,故以此为例。
C语言具有较为完备的静态类型系统,类型可以通过组合产生新的类型 。C的类型包括基本类型和构造类型两种(指针和函数可以看作构造类型)。但是C语言在类型系统上存在着一些不可忽视的缺陷。根据上文中我们介绍类型系统的时候给出的定义, C语言是一种弱类型语言,这会导致在编译过程中的检查是有一定缺陷的,也就有可能会导致程序运行时的安全问题。其中比较常见的不安全运算就是指针运算和类型强制转换。
我们在此处再次举出上课时用到的例子[7]:

typedef struct{int m;float a[];} record;
	record p={2,{1.0,2.0}},q={3,{1.0,2.0,3.0}};
	int main(){
		p=q;
		printf(“%d %f %f %f\n”,p.n,p.a[0],p.a[1],p.a[2]);
		printf(“%d %f %f %f\n”,q.n,q.a[0],q.a[1],q.a[2]);
      return 0;
	}

对于这样一段程序,经GCC: (Ubuntu/Linaro 4.6.3-1 ubuntu5)4.6.3编译后会出现以下结果:
3 1.000000 2.000000 3.000000
3432433534 1.000000 2.000000 3.000000
q.n被破坏的原因在于编译器分配连续的内存来存放结构体p和q的数据,导致p.a[1]和q.n的地址相邻,在执行过p=q之后, p.a[2]占用了q.n的地址,q.n的中存储的值被破坏。
又,下面是一个类型转换的例子
/一个初学C会遇到的一个摄氏度转换的算法
f为输入的温度,c为转化后的温度
/

main()
{
     float c,f;
     scanf("%f",&f);
     c=5*(f-32)/9;
}
//根据优先级问题,先算括号里面的,即f-32,由于f是float型,则f-32也是float型
//5和9都是int型,5*(f-32)就是int型与float的乘积,此时的int会自动转换为
//float型
//同理,再除9,最终c为float型。

但是实际中经常会出现的问题是,有些同学会把 c=5*(f-32)/9的公式改写成 

c=5/9*(f-32)

即先用5/9再乘括号里的东西,这样的话输出结果会直接为0,因为5和9均为int型,由于int/int=in,所以5/9=0 ,0乘任何数都为0。这样就导致了错误。
弱类型语言与强类型语言并无法判别优劣。在实际工作中,使用哪种语言按需而定。对类型的弱化很多时候提高了语言的灵活性,在编写简单小应用时,使用弱类型语言可,节省很多代码量,有更高的开发效率。例如,C语言的类型系统存在缺陷,这种缺陷造成了C安全上的漏洞,但我们出于效率上的考虑允许了这种不足,所以C语言至今为仍最流行的语言之一。而对于构建大型项目,使用强类型语言可能会比使用弱类型更加规范可靠。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 编译器概述 第2章 词法分析 2.1 词法记号及属性 2.1.1 词法记号、模式、词法单元 2.1.2 词法记号的属性 2.1.3 词法错误 2.2 词法记号的描述与识别 2.2.1 串和语言 2.2.2 正规式 2.2.3 正规定义 2.2.4 状态转换图 2.3 有限自动机 2.3.1 不确定的有限自动机 2.3.2 确定的有限自动机 2.3.3 NFA到DFA的变换 2.3.4 DFA的化简 2.4 从正规式到有限自动机 2.5 词法分析器的生成器 第3章 语法分析 3.1 上下文无关文法 3.1.1上下文无关文法的定义 3.1.2 推导 3.1.3 分析树 3.1.4 二义性 3.2 语言和文法 3.2.1 正规式和上下文无关文法的比较 3.2.2分离词法分析器的理由 3.2.3 验证文法产生的语言 3.2.4 适当的表达式文法 3.2.5 消除二义性 3.2.6 消除左递归 3.2.7 提左因子 3.2.8 非上下文无关的语言结构 3.2.9 形式语言鸟瞰 3.3 自上而下分析 3.3.1 自上而下分析的一般方法 3.3.2 LL(1)文法 3.3.3 递归下降的预测分析 3.3.4 非递归的预测分析 3.3.5 构造预测分析表 3.3.6 预测分析的错误恢复 3.4 自下而上分析 3.4.1 归约 3.4.2 句柄 3.4.3 用栈实现移进归约分析 3.4.4 移进归约分析的冲突 3.5 LR分析器 3.5.1 LR分析算法 3.5.2 LR文法和LR分析方法的特点 3.5.3 构造SLR分析表 3.5.4 构造规范的LR分析表 3.5.5 构造LALR分析表 3.5.6 非LR的上下文无关结构 3.6 二义文法的应用 3.6.1 使用文法以外的信息来解决分析动作的冲突 3.6.2特殊情况产生式引起的二义性 3.6.3 LR分析的错误恢复 3.7 分析器的生成器 3.7.1 分析器的生成器Yacc 3.7.2 用Yacc处理二义文法 3.7.3 Yacc的错误恢复 第4章 语法制导的翻译 4.1 语法制导的定义 4.1.1 语法制导定义的形式 4.1.2 综合属性 4.1.3 继承属性 4.1.4 属性依赖图 4.1.5 属性计算次序 4.2 S属性定义的自下而上计算 4.2.1 语法树 4.2.2 构造语法树的语法制导定义 4.2.3 S属性的自下而上计算 4.3 L属性定义的自上而下计算 4.3.1 L属性定义 4.3.2 翻译方案 4.3.3 预测翻译器的设计 4.3.4 用综合属性代替继承属性 4.4 L属性的自下而上计算 4.4.1 删除翻译方案中嵌入的动作 4.4.2 分析栈上的继承属性 4.4.3 模拟继承属性的计算 4.5 递归计算 4.5.1 自左向右遍历 4.5.2 其他遍历方法 4.5.3 多次遍历 第5章 类型检查 5.1 类型在程序设计语言中的作用 5.1.1 引言 5.1.2 执行错误和安全语言 5.1.3 类型化语言的优点 5.2 描述类型系统的语言 5.2.1 定型断言 5.2.2 定型规则 5.2.3 类型检查和类型推断 5.3 简单类型检查器的说明 5.3.1 一个简单的语言 5.3.2 类型系统 5.3.3 类型检查 5.3.4 类型转换 *5.4 多态函数 5.4.1 为什么要使用多态函数 5.4.2 类型变量 5.4.3 一个含多态函数的语言 5.4.4 代换、实例和合一 5.4.5 多态函数的类型检查 5.5 类型表达式的等价 5.5.1 类型表达式的结构等价 5.5.2 类型表达式的名字等价 5.5.3 记录类型 5.5.4 类型表示中的环 5.6 函数和算符的重载 5.6.1 子表达式的可能类型集合 5.6.2 缩小可能类型的集合 第6章 运行时存储空间的组织和管理 6.1 局部存储分配策略 6.1.1 过程 6.1.2 名字的作用域和绑定 6.1.3 活动记录 6.1.4 局部数据的安排 6.1.5 程序块 6.2 全局存储分配策略 6.2.1 运行时内存的划分 6.2.2 静态分配 6.2.3 栈式分配 6.2.4 堆式分配 6.3 非局部名字的访问 6.3.1 无过程嵌套的静态作用域 6.3.2 有过程嵌套的静态作用域 6.3.3 动态作用域 6.4 参数传递 6.4.1值调用 6.4.2 引用调用 6.4.3 复写-恢复调用 6.4.4 换名调用 第7章 中间代码生成 7.1 中间语言 7.1.1 后缀表示 7.1.2 图形表示 7.1.3 三地址代码 7.2 声明语句 7.2.1 过程中的声明 7.2.2 作用域信息的保存 7.2.3 记录的域名 7.3 赋值语句 7.3.1 符号表中的名字 7.3.2 临时名字的重新使用 7.3.3 数组元素的地址计算 7.3.4 数组元素地址计算的翻译方案 7.3.5 类型转换 7.4 布尔表达式和控制流语句 7.4.1 布尔表达式的翻译 7.4.2 控制流语句的翻译 7.4.3 布尔表达式的控制流翻译 7.4.4 开关语句的翻译 7.4.5 过程调用的翻译 第8章 代码生成 8.1 代码生成器设计中的问题 8.1.1 目标程序 8.1.2 指令选择 8.1.3 寄存器分配 8.1.4 计算次序选择 8.2 目标机器 8.2.1 目标机器的指令系统 8.2.2 指令的代价 8.3 基本块和流图 8.3.1 基本块 8.3.2 基本块的变换 8.3.3 流图 8.3.4 下次引用信息 8.4 一个简单的代码生成器 8.4.1 寄存器描述和地址描述 8.4.2 代码生成算法 8.4.3 寄存器选择函数 8.4.4 为变址和指针语句产生代码 8.4.5 条件语句 *第9章 代码优化 9.1 优化的主要种类 9.1.1 代码改进变换的标准 9.1.2 公共子表达式删除 9.1.3 复写传播 9.1.4 死代码删除 9.1.5 代码外提 9.1.6 强度削弱和归纳变量删除 9.1.7 优化编译器的组织 9.2 流图中的循环 9.2.1 必经结点 9.2.2 自然循环 9.2.3 前置结点 9.2.4 可归约流图 9.3 全局数据流分析介绍 9.3.1 点和路径 9.3.2 到达-定值 9.3.3 可用表达式 9.3.4 活跃变量分析 9.4 代码改进变换 9.4.1公共子表达式删除 9.4.2复写传播 9.4.3 寻找循环不变计算 9.4.4 代码外提 9.4.5 归纳变量删除 第10章 编译系统和运行系统 10.1 C语言的编译系统 10.1.1 预处理器 10.1.2 汇编器 10.1.3 连接器 10.1.4 目标文件的格式 10.1.5 符号解析 10.1.6 静态库 10.1.7 可执行目标文件及装入 10.1.8 动态连接 10.1.9 处理目标文件的一些工具 10.2 Java语言的运行系统 10.2.1 Java虚拟机语言简介 10.2.2 Java虚拟机 10.2.3即时编译器 *10.3 无用单元收集 10.3.1 标记和清扫 10.3.2 引用计数 10.3.3 拷贝收集 10.3.4 分代收集 10.3.5 渐增式收集 10.3.6 编译器与收集器之间的相互影响 *第11章 面向对象语言的编译 11.1 面向对象语言的概念 11.1.1 对象和对象类 11.1.2 继承 11.1.3 信息封装 11.2 方法的编译 11.3 继承的编译方案 11.3.1 单一继承的编译方案 11.3.2 重复继承的编译方案 *第12章 函数式语言的编译 12.1 函数式程序设计语言简介 12.1.1 语言构造 12.1.2 参数传递机制 12.1.3 变量的自由出现和约束出现 12.2 函数式语言的编译简介 12.2.1 几个受启发的例子 12.2.2 编译函数 12.2.3 环境与约束 12.3 抽象机的系统结构 12.3.1 抽象机的栈 12.3.2 抽象机的堆 12.3.3 名字的寻址 12.3.4 约束的建立 12.4 指令集和编译 12.4.1 表达式 12.4.2 变量的引用性出现 12.4.3 函数定义 12.4.4 函数应用 12.4.5 构造和计算闭包 12.4.6 letrec表达式和局部变量
中文名: 编译原理 作者: 陈意云 张昱 资源格式: PDF 版本: 文字版 出版社: 高等教育出版社书号: 9787040133677发行时间: 2003年09月 地区: 大陆 语言: 简体中文 简介: 内容简介: 本书介绍编译器构造的一般原理和基本实现方法,主要内容包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等。除了介绍命令式编程语言的编译技术外, 本书还介绍面向对象语言和函数式编程语言的实现技术。本书还强调一些相关的理论知识, 如形式语言和自动机理论、语法制导的定义和属性文法、类型论和类型系统等。 本书取材广泛新颖、图文并茂,注意理论联系实际。本书可作为高等学校计算机科学及相关专业的教材,也可供计算机软件工程技术人员参考使用。 目录: 第1章 编译器概述 1.1 词法分析 1.2 语法分析 1.3 语义分析 1.4 中间代码生成 1.5 代码优化 1.6 代码生成 1.7 符号表管理 1.8 错误诊断和报告 1.9 阶段的分组 习题1 第2章 词法分析 2.1 词法记号及属性 2.1.1 词法记号、模式、词法单元 2.1.2 词法记号的属性 2.1.3 词法错误 2.2 词法记号的描述与识别 2.2.1 串和语言 2.2.2 正规式 2.2.3 正规定义 2.2.4 状态转换图 2.3 有限自动机 2.3.1 不确定的有限自动机 2.3.2 确定的有限自动机 2.3.3 NFA到DFA的变换 2.3.4 DFA的化简 2.4 从正规式到有限自动机 2.5 词法分析器的生成器 习题2 第3章 语法分析 3.1 上下文无关文法 3.1.1 上下文无关文法的定义 3.1.2 推导 3.1.3 分析树 3.1.4 二义性 3.2 语言和文法 3.2.1 正规式和上下文无关文法的比较 3.2.2 分离词法分析器的理由 3.2.3 验证文法产生的语言 3.2.4 适当的表达式文法 3.2.5 消除二义性 3.2.6 消除左递归 3.2.7 提左因子 3.2.8 非上下文无关的语言结构 3.2.9 形式语言鸟瞰 3.3 自上而下分析 3.3.1 自上而下分析的一般方法 3.3.2 LL(1)文法 3.3.3 递归下降的预测分析 3.3.4 非递归的预测分析 3.3.5 构造预测分析表 3.3.6 预测分析的错误恢复 3.4 自下而上分析 3.4.1 归约 3.4.2 句柄 3.4.3 用栈实现移进一归约分析 3.4.4 移进一归约分析的冲突 3.5 LR分析器 3.5.1 LR分析算法 3.5.2 LR文法和LR分析方法的特点 3.5.3 构造sLR分析表 3.5.4 构造规范的LR分析表 3.5.5 构造LALR分析表 3.5.6 非LR的上下文无关结构 3.6 二义文法的应用 3.6.1 使用文法以外的信息来解决分析动作的冲突 3.6.2 特殊情况产生式引起的二义性 3.6.3 IR分析的错误恢复 3.7 分析器的生成器 3.7.1 分析器的生成器Yacc 3.7.2 用Yaec处理二义文法 3.7.3 Yaec的错误恢复 习题3 第4章 语法制导的翻译 4.1 语法制导的定义 4.1.1 语法制导定义的形式 4.1.2 综合属性 4.1.3 继承属性 4.1.4 属性依赖图 4.1.5 属性计算次序 4.2 s属性定义的自下而上计算 4.2.1 语法树 4.2.2 构造语法树的语法制导定义 4.2.3 S属性的自下而上计算 4.3 L属性定义的自上而下计算 4.3.1 L属性定义 4.3.2 翻译方案 4.3.3 预测翻译器的设计 4.3.4 用综合属性代替继承属性 4.4 L属性的自下而上计算 4.4.1 删除翻译方案中嵌入的动作 4.4.2 分析栈上的继承属性 4.4.3 模拟继承属性的计算 4.5 递归计算 4.5.1 自左向右遍历 4.5.2 其他遍历方法 4.5.3 多次遍历 习题4 第5章 类型检查 5.1 类型在程序设计语言中的作用 5.1.1 引言 5.1.2 执行错误和安全语言 5.1.3 类型化语言的优点 5.2 描述类型系统的语言 5.2.1 定型断言 5.2.2 定型规则 5.2.3 类型检查和类型推断 5.3 简单类型检查器的说明 5.3.1 一个简单的语言 5.3.2 类型系统 5.3.3 类型检查 5.3.4 类型转换 5.4 多态函数 5.4.1 为什么要使用多态函数 5.4.2 类型变量 5.4.3 一个含多态函数的语言 5.4.4 代换、实例和合 5.4.5 多态函数的类型检查 5.5 类型表达式的等价 5.5.1 类型表达式的结构等价 5.5.2 类型表达式的名字等价 5.5.3 记录类型 5.5.4 类型表示中的环 5.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值