强类型语言和静态类型语言有什么区别?

603

此外,这两者是否相互暗示?


12
了解,以下是需要翻译的内容:请注意,https://dev59.com/03E95IYBdhLWcg3wWMdQ 有相似的问题。 - Norman Ramsey
9
Tcl 是强类型语言。它只有字符串类型 :P - nawfal
53
@nawfal 不,那是强类型的 ;) - geekonaut
1
你们知道什么是 Tcl 吗? - carloswm85
1
@NathanielJones 感谢您的回复。TCL也是一个品牌。 ;) 这是编程语言的参考资料:https://en.wikipedia.org/wiki/Tcl - carloswm85
9个回答

738
什么是强类型语言和静态类型语言之间的区别?
静态类型语言具有在实现(编译器或解释器)编译时检查的类型系统。类型检查会拒绝一些程序,并且通过检查的程序通常带有一些保证;例如,编译器保证不对浮点数使用整数算术指令。
关于“强类型”的定义并没有真正的共识,尽管专业文献中最广泛使用的定义是,在“强类型”语言中,程序员无法绕过类型系统所施加的限制。这个术语几乎总是用来描述静态类型语言。
静态 vs 动态
静态类型的相反是“动态类型”,这意味着:
1. 在运行时使用的值被归类为类型。 2. 对这些值的使用有限制。 3. 当违反这些限制时,将报告为(动态)类型错误。
例如,Lua 是一种动态类型语言,它有字符串类型、数字类型、布尔类型等。在 Lua 中,每个值都属于恰好一个类型,但这并不是所有动态类型语言的要求。在 Lua 中,允许连接两个字符串,但不允许连接字符串和布尔值。
强 vs 弱
"强类型"的相反概念是"弱类型",这意味着你可以绕过类型系统。C语言以弱类型著称,因为通过强制转换,任何指针类型都可以转换为任何其他指针类型。Pascal旨在成为强类型语言,但设计中的一个疏漏(未标记的变体记录)为类型系统引入了漏洞,因此从技术上讲,它是弱类型的。真正强类型语言的例子包括CLU、Standard ML和Haskell。实际上,Standard ML已经进行了几次修订,以消除在广泛部署该语言后发现的类型系统漏洞。
"强"和"弱"之间的区别实际上没有那么有用。一个类型系统是否有漏洞不如漏洞的确切数量和性质、它们在实践中出现的可能性以及利用漏洞的后果重要。实际上,最好完全避免使用"强"和"弱"这些术语,因为:
- 新手经常将它们与"静态"和"动态"混淆。 - 显然,一些人使用"弱类型"来谈论隐式转换的相对普遍性或缺失。 - 专业人士无法就术语的确切含义达成一致。 - 总的来说,你不太可能向你的受众提供信息或启示。
很遗憾,就类型系统而言,“强类型”和“弱类型”并没有普遍公认的技术含义。如果想讨论类型系统的相对强度,最好讨论确切地提供了哪些保证以及未提供哪些保证。例如,可以问这样一个好问题:“是否保证给定类型(或类)的每个值都是通过调用该类型的构造函数创建的?” 在 C 中的答案是否定的。在 CLU、F#和 Haskell 中是肯定的。关于 C++,我不确定,我想知道。
相比之下,静态类型意味着在程序执行之前进行检查,并且程序可能在启动之前被拒绝。动态类型意味着值的类型在执行期间进行检查,而糟糕的类型操作可能会导致程序停止运行或发出错误信号。静态类型的主要原因是排除可能存在“动态类型错误”的程序。
“它们是否互为蕴含关系?”在学究式的层面上,答案是否定的,因为“强类型”这个词实际上没有任何意义。但在实践中,人们几乎总是做以下两件事之一:
  • 他们(不正确地)使用“强类型”和“弱类型”来表示“静态类型”和“动态类型”,因此他们(不正确地)将“强类型”和“静态类型”交替使用。

  • 他们使用“强类型”和“弱类型”来比较静态类型系统的属性。很少听到有人谈论“强类型”或“弱类型”的动态类型系统。除了FORTH之外,我想不出有哪种动态类型语言的类型系统可以被破坏。从某种意义上讲,在执行引擎中内置了这些检查,并且在执行每个操作之前都会进行检查。

无论如何,如果某人称一种语言为“强类型”,那么这个人很可能在谈论一种静态类型的语言。


4
@Adam:显然不足以得到投票支持的正确性 :)因为Cletus的回答包含了很多误解(虽然我编辑掉了最糟糕的部分),我感到有必要用一音节单词来详细说明每件事情… - Norman Ramsey
2
我给你点了赞 :) 即使是"编译"这个词在今天运行动态语言的虚拟机中也不是一个清晰的概念。从技术上讲,Java和C#都会被编译两次(即时编译),并且都会进行一些类型分析。像在.NET虚拟机中运行的Javascript这样的语言可能更加类型安全,因为有虚拟机的存在。 - Adam Gent
7
我现在很困惑!好的,亲爱的角斗场大斗士们,像我这样的可怜之人可以用以下简单的理解吗?1.静态:值与类型在编译时关联,而不是运行时。2.动态:值在运行时与类型相关联,因此值的类型可以在运行时更改,因此更容易发生与类型转换相关的问题。3.强/弱:别管它!这些不是技术术语,只是不好的命名法。这取决于谈论的上下文是什么。我能用这个简单的理解继续我的生活吗? :( - Saurabh Patil
1
“给定类型(或类)的每个值都保证是通过调用该类型的构造函数之一创建的吗?”在C语言中,答案是否定的。能否提供一个在C语言中出现这种情况的示例情况?我想这涉及将指针转换为结构体? - corazza
@NormanRamsey 对于C++,我不确定——我想知道:它和C一样。一个人可以轻易地破坏类型系统,但这样做大多数情况下会得到未定义的行为。 - legends2k
显示剩余5条评论

305

这个经常被误解,让我来澄清一下。

静态/动态类型

静态类型是指类型与变量绑定。类型在编译时检查。

动态类型是指类型与绑定。类型在运行时检查。

所以例如在Java中:

String s = "abcd";

s 将永远是一个 String。在其生命周期中,它可能指向不同的 String(因为在 Java 中,s 是一个引用)。它可能具有 null 值,但它永远不会引用一个 IntegerList。这就是静态类型。

在 PHP 中:

$s = "abcd";          // $s is a string
$s = 123;             // $s is now an integer
$s = array(1, 2, 3);  // $s is now an array
$s = new DOMDocument; // $s is an instance of the DOMDocument class

这就是动态类型。

强类型/弱类型

(编辑提醒!)

强类型是一个没有广泛意义的短语。大多数程序员使用这个术语来表示除了静态类型之外还意味着编译器执行的类型规则。例如,CLU有一个强大的类型系统,它不允许客户端代码使用类型提供的构造函数以外的方式创建抽象类型的值。 C具有相对强的类型系统,但可以“破坏”到一定程度,因为程序始终可以将一个指针类型的值转换为另一个指针类型的值。所以在C中,您可以取malloc()返回的值并欢快地将其转换为FILE*,编译器不会试图阻止您--甚至不会警告您正在做任何可疑的事情。

(原答案提到了某些关于值“不会在运行时更改类型”的内容。我认识很多语言设计者和编译器编写者,他们没有一个人谈论过值在运行时更改类型的事情,除非是一些非常高级的类型系统研究,在那里这被称为“强制更新问题”。)

弱类型意味着编译器不执行类型规则,或者说强制执行很容易被破坏。

这个答案的原始版本混淆了弱类型和隐式转换(有时也称为“隐式提升”)。例如,在Java中:

String s = "abc" + 123; // "abc123";

这段代码是隐式提升的一个例子:在与"abc"连接之前,123被隐式转换为字符串。可以认为Java编译器将该代码重写为:

String s = "abc" + new Integer(123).toString();

考虑一个经典的 PHP “以...开始”的问题:

if (strpos('abcdef', 'abc') == false) {
  // not found
}
这里的错误在于strpos()返回匹配的索引,而索引为0。0被强制转换为布尔值false,因此条件实际上为真。解决方法是使用===而不是==来避免隐式转换。

这个例子说明了隐式转换和动态类型的结合会让程序员走上错误的道路。

与Ruby相比如下:

val = "abc" + 123

这是一个运行时错误,因为在Ruby中,对象123并不会因为被传递到+方法中就隐式转换。在Ruby中,程序员必须显式地进行转换:

val = "abc" + 123.to_s
比较PHP和Ruby是一个很好的例子。它们都是动态类型语言,但PHP有很多隐式转换,而Ruby则没有(如果你不熟悉Ruby,这可能会让你感到惊讶)。
静态/动态与强/弱之间是相互独立的。人们可能会混淆它们,部分原因是强/弱类型不仅没有明确定义,而且在强和弱的确切含义上也不存在真正的共识。因此,强/弱类型更多的是一种灰色地带,而不是黑白分明的。
所以回答你的问题:另一种看待这个问题的方式是,静态类型是编译时类型安全,强类型是运行时类型安全,这个说法“大体”是正确的。
这是因为在静态类型语言中,变量必须声明类型,并且可以在编译时检查类型。强类型语言具有在运行时具有类型的值,并且程序员很难在没有动态检查的情况下破坏类型系统。
但是重要的是要理解,一个语言可以是静态/强类型、静态/弱类型、动态/强类型或动态/弱类型。

与其说$s是整数或字符串,不如说类型与“abcd”或1234相关联,而不是与变量$s相关联。 - Srinivas Reddy Thatiparthy
优秀的回答,提供了清晰的例子。然而,我认为它并没有完全解决人们为什么会将强类型和静态类型作为一对概念来询问的困惑。例如,原帖中的措辞“静态类型是否意味着强类型?”你的回答强调了它们的独立性。为了继续澄清为什么强类型经常与静态类型成对出现,Norman Ramsey之前的回答非常好:https://dev59.com/PXRC5IYBdhLWcg3wMeLg - JasDev
1
在 Ruby 中,"abc" + 123 是一种运行时错误,而不是编译错误。如果它是编译错误,那么 Ruby 就会是静态类型的。 - sepp2k
弱类型示例需要改进(请参见我的答案),但格式很好。 - Adam Gent
在我看来,强类型和弱类型的区别在于:强类型:"c" + True = 运行时错误或编译时错误。弱类型:"c" + True = "b" 或 "d",因为所有内容都被视为原始字节。强类型语言有 C#、Ruby 和 C++,而弱类型语言则包括汇编语言和 C 语言(由于隐式 void 指针)。 - Jonathan Allen
这种回答劫持是否可以?中间有一个奇怪的段落关于“原始答案”,我认为作者的原意在很多地方被编辑改变了。 - neonblitzer

59

两者均位于不同轴线上:

  • 强类型与弱类型
  • 静态类型与动态类型

强类型意味着变量不会自动转换为另一种类型。弱类型则相反:Perl可以将一个字符串像"123"在数字环境中使用,并自动将其转换为int 123。像Python这样的强类型语言不会这样做。

静态类型意味着编译器在编译时确定每个变量的类型。动态类型语言只在运行时才确定变量的类型。


12
我必须表示不同意。强类型语言是指在运行时了解类型的语言。弱类型语言就像汇编语言一样,不了解类型。你的例子处于第三个维度上,即“隐式转换与显式转换”。 - Jonathan Allen
2
其实我同意Jonathan的观点,但是如果你进行完整的静态分析并且不允许强制转换,你就不必在运行时可用类型才能保持强类型(请参见我的编辑答案)。 - Adam Gent
5
Python 是动态类型和强类型语言的一个例子。 - MaRoBet

41

强类型指类型之间的转换受到限制。

静态类型意味着类型不是动态的 - 一旦变量被创建,就无法更改其类型。


2
为了证明这一点:在强类型语言中,您不能将“5”== 5进行比较并使其为真:字符串不是整数。如果我没记错的话,大多数现代脚本语言都是强动态类型的。然而,Tcl/Tk是弱类型的 - 一切都可以被视为字符串。 - Little Bobby Tables
在弱类型语言中,“5” == 5 被解读为 0x35 == 0x05。换句话说,所有内容都被视为原始字节。 - Jonathan Allen
2
我必须不同意你们两个。以Lua为例,你可以比较"5" == 5,它会返回false,但是通过快速转换,可以通过"5" + 0来实现。 - RCIX

17
答案已在上面给出。尝试区分强类型与弱类型以及静态类型与动态类型的概念。

什么是强类型与弱类型?

强类型:不会自动从一种类型转换为另一种类型。

像 Go 或 Python 这样的强类型语言,"2" + 8 将引发类型错误,因为它们不允许 "类型强制转换"。

弱类型:将自动转换为一种类型到另一种类型:

像 JavaScript 或 Perl 这样的弱类型语言不会抛出错误,在这种情况下 JavaScript 会返回 '28',Perl 则会返回 10。

Perl 示例:

my $a = "2" + 8;
print $a,"\n";

将它保存为main.pl并运行perl main.pl,您将获得输出10。

什么是静态类型和动态类型?

在编程中,程序员使用静态类型和动态类型来描述变量类型检查的时间点。静态类型语言是指在编译时进行类型检查的语言,而动态类型语言是指在运行时进行类型检查的语言。

  • 静态:在运行时之前进行类型检查
  • 动态:在执行期间实时检查类型

这意味着什么?

在Go中,它会在运行时之前进行类型检查(静态检查)。这意味着它不仅会翻译和检查代码,而且还会扫描所有代码,并且在代码运行之前就会抛出类型错误。例如:

package main

import "fmt"

func foo(a int) {
    if (a > 0) {
        fmt.Println("I am feeling lucky (maybe).")
    } else {
        fmt.Println("2" + 8)
    }
}

func main() {
    foo(2)
}

将此文件保存在main.go中并运行它,您将收到编译失败的消息。

go run main.go
# command-line-arguments
./main.go:9:25: cannot convert "2" (type untyped string) to type int
./main.go:9:25: invalid operation: "2" + 8 (mismatched types string and int)

但是这种情况在Python中无效。例如,以下代码块将执行第一次foo(2)调用,并且将在第二次foo(0)调用时失败。这是因为Python是动态类型的,它只翻译和检查正在执行的代码的类型。else块从不对foo(2)执行,因此“2”+8从未被考虑过,在foo(0)调用中,它将尝试执行该块并失败。

def foo(a):
    if a > 0:
        print 'I am feeling lucky.'
    else:
        print "2" + 8
foo(2)
foo(0)

您将看到以下输出

python main.py
I am feeling lucky.
Traceback (most recent call last):
  File "pyth.py", line 7, in <module>
    foo(0)
  File "pyth.py", line 5, in foo
    print "2" + 8
TypeError: cannot concatenate 'str' and 'int' objects

请删除这个回答。那不是强类型的意思。 - Andy Ray
请删除这个回答。那不是强类型的意思。 - undefined

13

数据类型转换并不一定意味着弱类型,因为有时它只是语法糖:

上述Java的例子并不代表它是弱类型语言,因为Java实际上是强类型语言。

String s = "abc" + 123;

不是弱类型示例,因为它确实在执行:

String s = "abc" + new Integer(123).toString()

如果你正在构建一个新对象,数据强制转换也不是弱类型的。 Java是弱类型的很糟糕的例子(任何具有良好反射的语言都可能不是弱类型的)。因为语言的运行时始终知道类型是什么(例外情况可能是原生类型)。

这与C不同。C是最好的弱类型示例之一。运行时不知道4个字节是整数、结构体、指针还是4个字符。

语言的运行时真正定义了它是否是弱类型,否则这只是观点。

编辑: 经过进一步思考,这并不一定正确,因为运行时不必将所有类型重新编写到运行时系统中才能成为强类型系统。 Haskell和ML具有完整的静态分析功能,因此可以从运行时中省略类型信息。


B 可能是一个更好的例子,虽然它不太出名。 - Tom Hawtin - tackline
JavaScript 也是一种弱类型语言,但由于其类型较少且无法真正构造新类型,因此如此。 - Adam Gent

12

两者并不意味着相同。对于一种被静态类型化的语言,这意味着在编译时可知或推断出所有变量的类型。

强类型的语言不允许您将一种类型用作另一种类型。C 是一个弱类型的语言,是强类型语言所不允许的良好例子。在 C 中,您可以传递错误类型的数据元素而不会报错。而在强类型语言中则不行。


8

强类型可能意味着变量具有明确定义的类型,并且在表达式中组合不同类型的变量时有严格的规则。例如,如果A是整数而B是浮点数,则A+B的严格规则可能是A转换为浮点数并将结果作为浮点数返回。如果A是整数而B是字符串,则严格规则可能是A+B无效。

静态类型可能意味着类型在编译时(或非编译语言的等效方式)分配,并且在程序执行期间不能更改。

请注意,这些分类不是互斥的,事实上我经常希望它们一起发生。许多强类型语言也是静态类型。

请注意,当我使用“可能”一词时,这是因为没有普遍接受的这些术语的定义。正如您到目前为止所看到的答案一样。


1
在我看来,最好完全避免使用这些定义,不仅因为这些术语没有达成共识的定义,而且已经存在的定义往往侧重于技术方面,例如:是否允许在混合类型上操作,如果不行是否有漏洞可以绕过限制,例如通过指针实现操作。

相反地,并再次强调这是一个观点,我们应该关注问题:类型系统是否使我的应用程序更可靠?这是一个特定于应用程序的问题。

例如:如果我的应用程序有一个名为 acceleration 的变量,那么显然,如果变量的声明和使用方式允许将值 "Monday" 赋给 acceleration,那就有问题了,因为明显加速度不能是平日(字符串)。

另一个例子:在 Ada 中,可以定义:subtype Month_Day is Integer range 1..31;,类型 Month_Day 是弱类型,因为它不是 Integer 的单独类型(因为它是子类型),但是它被限制在范围 1..31 内。相比之下:type Month_Day is new Integer; 将创建一个不同的类型,这是强类型,因为它不能与整数混合使用而无需显式转换 - 但是它没有受到限制,可以接受无意义的值 -17。因此,从技术上讲,它更强大,但是可靠性较低。当然,可以声明 type Month_Day is new Integer range 1..31; 创建一个既不同又受限的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接