img

资料整理

弱类型、强类型、动态类型、静态类型语言的区别是什么?

通俗的方式理解动态类型,静态类型;强类型,弱类型

正确性存疑,只是作为整理。

参考资料

  1. 编程语言的类型系统为何如此重要?
  2. 编程语言傻傻分不清:弱类型、强类型、动态类型、静态类型

前置概念

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。

img

从图中可以看出,绿色的 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隐式类型

img

动态类型语言(Dynamically Typed Language):

运行期间才做数据类型检查的语言,即动态类型语言编程时,永远不用给任何变量指定数据类型。该语言会在第一次赋值给变量时,在内部将数据类型记录下来。
例如:ECMAScript(JavaScript)、Ruby、Python、VBScript、php
Python和Ruby就是典型动态类型语言,其他各种脚本语言如VBScript也多少属于动态类型语言
优点:方便阅读,不需要写非常多的类型相关的代码;
缺点:不方便调试,命名不规范时会造成读不懂,不利于理解等

静态类型语言(Statically Typed Language):

编译期间做检查数据类型的语言,即写程序时要声明所有变量的数据类型,是固定的。使用数据之前,必须先声明数据类型(int ,float,double等)。相当于使用之前,首先要为它们分配好内存空间。
例如:C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等
优点:结构非常规范,便于调试,方便类型安全
缺点:为此需要写更多类型相关代码,不便于阅读、不清晰明了

示例

弱类型:

1
2
> "1"+2
'12'

强类型:

1
2
3
4
>>> "1"+2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

动态类型:

1
2
3
4
5
6
>>> a = 1
>>> type(a)
<type 'int'>
>>> a = "s"
>>> type(a)
<type 'str'>

静态类型:

1
2
3
4
5
6
Prelude> let a = "123" :: Int

<interactive>:2:9:
Couldn't match expected type `Int' with actual type `[Char]'
In the expression: "123" :: Int
In an equation for `a': a = "123" :: Int

结论

img

定义了一个某一个类型的数据,通常意味着以下三件事:

1,定义了一块某种结构的存储空间,用来存储这个数据;

2,特定类型意味可以对这块存储空间进行一些特定的操作;

3,声明了一个标识符(不严密的说就是变量名),作为操作这块数据的“标签”,之后可以通过这个标签操作这块数据,否则这块数据是无法使用的。

无论编译型语言还是解释型语言,这三点是共性。

若从程序出错的角度来解释这个问题,和类型有关的错误有:

1,访问了一个特定类型的标识符,但是访问的不是预期的存储空间。

2,对特定类型的标识符对应的数据,进行了非预期的操作。

为了防止与类型有关的错误发生,编程语言可以进行类型检查。

1、强类型检查弱类型检查

程序错误,大体上可分为可捕获错误trapped errors 和不可捕获错误untrapped errors。 可捕获错误trapped errors 发生时,程序停了下来,用户明确知道发生了错误;而不可捕获错误untrapped errors发生时,用户不知道错误的发生,程序的结果是否正确也无法判断,有可能造成重大损失。所以类型检查的目标,是:“捕获与类型有关的错误”。

所以,能保证不存在与类型有关的不可捕获错误的编程语言,可以称之为 具有强类型检查;反之称为 弱类型检查

2、动态类型检查静态类型检查

普遍说法是静态类型检查是指在编译阶段进行的类型检查,动态类型检查是在执行阶段进行的类型检查。那么严格来说,解释性语言,并没有编译阶段。(虽然为了效率通常会转义成中间代码,这其中存在一些灰色地带,但是这个转义的过程,最基本的要求是对用户透明。)所以按”编译阶段”的说法,对于解释型语言,并不存在静态类型检查的概念。但是,许多解释型语言,现在往往可以通过外置的lint工具对脚本进行检查,这也是一种静态检查,但是已经不是语言本身的特性了。

对于编译型语言来说,可以在编译阶段进行静态类型检查,而且因为运行时库的存在,也可以在运行时进行动态类型检查。

3、标识符动态类型绑定静态类型绑定

特定类型数据需要一个标识符(不严密的说就是变量名)与其绑定。这里面涉及的概念比较复杂:

  • 标识符与特定类型的数据绑定后,不允许解绑,那怕是解绑后重新绑定到同样类型的数据。

  • 标识符与特定类型的数据绑定后,允许解绑,但是解绑后只允许重新绑定到同样类型的数据。

  • 标识符与特定类型的数据绑定后,允许解绑,解绑后允许绑定到任意类型的数据。

4、对于编译型语言,上述绑定,理论上可以发生在编译阶段,也可以发生在执行阶段。在执行阶段,变量名往往并不存在,存在的只是指针,这也是一种隐含的动态绑定,意味这一个指针指向了一个新的类型的数据空间,这有极大的便利性灵活性,也有极大的危险性。C语言看似非常“静态”,其实动态的一塌糊涂。

上述情况中的3,是通常意义上所说的动态类型绑定。动态类型绑定的主要问题是,同一个标识符的多次绑定,容易混淆本来的意义。动态绑定的表示符,往往通过推到来确定类型,这也会造成程序的可读性很差。

所以,为了搞清楚“弱类型、强类型、动态类型、静态类型”这四个提法,关键要这个提法就容易让人迷惑,不如搞清楚这三对概念:

弱类型检查强类型检擦

动态类型检查静态类型检查

标识符的 静态类型绑定动态类型绑定

留言

⬆︎TOP