厘清概念:弱类型、强类型、动态类型、静态类型
资料整理
正确性存疑,只是作为整理。
参考资料
前置概念
Program Errors
- trapped errors。导致程序终止执行,如除0,Java中数组越界访问
- untrapped errors。 出错后继续执行,但可能出现任意行为。如C里的缓冲区溢出、Jump到错误地址
Forbidden Behaviours
语言设计时,可以定义一组forbidden behaviors. 它必须包括所有untrapped errors, 但可能包含trapped errors.
well behaved & ill behaved
如果程序执行不可能出现forbidden behaviors,则为well behaved,否则为ill behaved。
从图中可以看出,绿色的 program 表示所有程序(所有程序,你能想到和不能想到的),error 表示出错的程序,error 不仅仅包括 trapped error 和 untrapped error。
根据图我们可以严格的定义动态类型,静态类型;强类型,弱类型
- 强类型:如果一门语言写出来的程序在红色矩形外部,则这门语言是强类型的,也就是上面说的 well behaved
- 弱类型:如果一门语言写出来的程序可能在红色矩形内部,则这门语言是弱类型的,也就是上面说的 ill behaved
- 静态类型:一门语言在编译时排除可能出现在红色矩形内的情况(通过语法报错),则这门语言是静态类型的
- 动态类型:一门语言在运行时排除可能出现在红色矩形内的情况(通过运行时报错,但如果是弱类型可能会触发 untrapped error,比如隐式转换,使得程序看起来似乎是正常运行的),则这门语言是动态类型的
参考:《Type Systems》 Luca Cardelli - Microsoft Research
Trapped error: An execution error that immediately results in a fault.
Untrapped error: An execution error that does not immediately result in a fault.
Forbidden error: The occurrence of one of a predetermined class of execution errors;
Typically the improper application of an operation to a value, such as not(3).
Well behaved: A program fragment that will not produce forbidden errors at run time.
Strongly checked language: A language where no forbidden errors can occur at run time (depending on the definition of forbidden error).
Weakly checked language: A language that is statically checked but provides no clear guarantee of absence of execution errors.
Statically checked language: A language where good behavior is determined before execution.
Dynamically checked language: A language where good behavior is enforced during execution.
Type safety: The property stating that programs do not cause untrapped errors.
Explicitly typed language: A typed language where types are part of the syntax.
Implicitly typed language: A typed language where types are not part of the syntax.
强弱类型
- 强类型strongly typed: 如果一种语言的所有程序都是well behaved——即不可能出现forbidden behaviors,则该语言为strongly typed。
- 弱类型weakly typed: 否则为weakly typed。比如C语言的缓冲区溢出,属于trapped errors,即属于forbidden behaviors..故C是弱类型
弱类型语言,类型检查更不严格,如偏向于容忍隐式类型转换。譬如说C语言的int可以变成double。 这样的结果是:容易产生forbidden behaviours,所以是弱类型的。
强类型定义语言(Explicit type conversion,强制数据类型定义语言,类型安全的语言):
一旦变量被指定某个数据类型,如果不经强制转换,即永远是此数据类型。
举例:若定义了一个整型变量a,若不进行显示转换,不能将a当作字符串类型处理
强类型语言是指需要进行变量/对象类型声明的语言,一般情况下需要编译执行。例如C/C++/Java/C#
弱类型定义语言(Implicit type conversion,类型不安全的语言):
数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
举例:在VBScript中,可以将字符串 ‘12’ 和整数 3 进行连接得到字符串 ‘123’, 然后可以把它看成整数 123,而不需要显示转换
例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等
注意:强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。
动静态类型
静态类型、动态类型其实是指Type Check发生的时机。
- 静态类型 statically: 如果在编译时拒绝ill behaved程序,则是statically typed;
- 动态类型dynamiclly: 如果在运行时拒绝ill behaviors, 则是dynamiclly typed。
只通过是否将类型名显式地写出来,如int a; 并不能判断动静态类型。如Ocaml是静态隐式类型。
静态类型可以分为两种:
- 如果类型是语言语法的一部分,在是explicitly typed显式类型;
- 如果类型通过编译时推导,是implicity typed隐式类型
动态类型语言(Dynamically Typed Language):
运行期间才做数据类型检查的语言,即动态类型语言编程时,永远不用给任何变量指定数据类型。该语言会在第一次赋值给变量时,在内部将数据类型记录下来。
例如:ECMAScript(JavaScript)、Ruby、Python、VBScript、php
Python和Ruby就是典型动态类型语言,其他各种脚本语言如VBScript也多少属于动态类型语言
优点:方便阅读,不需要写非常多的类型相关的代码;
缺点:不方便调试,命名不规范时会造成读不懂,不利于理解等
静态类型语言(Statically Typed Language):
编译期间做检查数据类型的语言,即写程序时要声明所有变量的数据类型,是固定的。使用数据之前,必须先声明数据类型(int ,float,double等)。相当于使用之前,首先要为它们分配好内存空间。
例如:C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等
优点:结构非常规范,便于调试,方便类型安全
缺点:为此需要写更多类型相关代码,不便于阅读、不清晰明了
示例
弱类型:
1 | > "1"+2 |
强类型:
1 | "1"+2 |
动态类型:
1 | 1 a = |
静态类型:
1 | Prelude> let a = "123" :: Int |
结论
定义了一个某一个类型的数据,通常意味着以下三件事:
1,定义了一块某种结构的存储空间,用来存储这个数据;
2,特定类型意味可以对这块存储空间进行一些特定的操作;
3,声明了一个标识符(不严密的说就是变量名),作为操作这块数据的“标签”,之后可以通过这个标签操作这块数据,否则这块数据是无法使用的。
无论编译型语言还是解释型语言,这三点是共性。
若从程序出错的角度来解释这个问题,和类型有关的错误有:
1,访问了一个特定类型的标识符,但是访问的不是预期的存储空间。
2,对特定类型的标识符对应的数据,进行了非预期的操作。
为了防止与类型有关的错误发生,编程语言可以进行类型检查。
1、强类型检查与弱类型检查;
程序错误,大体上可分为可捕获错误trapped errors 和不可捕获错误untrapped errors。 可捕获错误trapped errors 发生时,程序停了下来,用户明确知道发生了错误;而不可捕获错误untrapped errors发生时,用户不知道错误的发生,程序的结果是否正确也无法判断,有可能造成重大损失。所以类型检查的目标,是:“捕获与类型有关的错误”。
所以,能保证不存在与类型有关的不可捕获错误的编程语言,可以称之为 具有强类型检查;反之称为 弱类型检查。
2、动态类型检查与静态类型检查;
普遍说法是静态类型检查是指在编译阶段进行的类型检查,动态类型检查是在执行阶段进行的类型检查。那么严格来说,解释性语言,并没有编译阶段。(虽然为了效率通常会转义成中间代码,这其中存在一些灰色地带,但是这个转义的过程,最基本的要求是对用户透明。)所以按”编译阶段”的说法,对于解释型语言,并不存在静态类型检查的概念。但是,许多解释型语言,现在往往可以通过外置的lint工具对脚本进行检查,这也是一种静态检查,但是已经不是语言本身的特性了。
对于编译型语言来说,可以在编译阶段进行静态类型检查,而且因为运行时库的存在,也可以在运行时进行动态类型检查。
3、标识符动态类型绑定与静态类型绑定;
特定类型数据需要一个标识符(不严密的说就是变量名)与其绑定。这里面涉及的概念比较复杂:
标识符与特定类型的数据绑定后,不允许解绑,那怕是解绑后重新绑定到同样类型的数据。
标识符与特定类型的数据绑定后,允许解绑,但是解绑后只允许重新绑定到同样类型的数据。
标识符与特定类型的数据绑定后,允许解绑,解绑后允许绑定到任意类型的数据。
4、对于编译型语言,上述绑定,理论上可以发生在编译阶段,也可以发生在执行阶段。在执行阶段,变量名往往并不存在,存在的只是指针,这也是一种隐含的动态绑定,意味这一个指针指向了一个新的类型的数据空间,这有极大的便利性灵活性,也有极大的危险性。C语言看似非常“静态”,其实动态的一塌糊涂。
上述情况中的3,是通常意义上所说的动态类型绑定。动态类型绑定的主要问题是,同一个标识符的多次绑定,容易混淆本来的意义。动态绑定的表示符,往往通过推到来确定类型,这也会造成程序的可读性很差。
所以,为了搞清楚“弱类型、强类型、动态类型、静态类型”这四个提法,关键要这个提法就容易让人迷惑,不如搞清楚这三对概念:
弱类型检查 与 强类型检擦
动态类型检查 与 静态类型检查
标识符的 静态类型绑定 与 动态类型绑定