null的目的是什么?

41

我正在参加编译器课程,我们的任务是从零开始创建自己的语言。目前我们的困惑是是否包括“null”类型。null有什么作用?我们团队中的一些人认为它并不是绝对必要的,而另一些人则赞成使用null来提供额外的灵活性。

你有什么想法,特别是支持或反对null吗? 您是否曾经创建需要使用null的功能?


感谢你让标题有意义,Patrick。 - Allyn
可能是 https://dev59.com/bHVC5IYBdhLWcg3wz0l9 的重复问题。 - nawfal
25个回答

48

Null: The Billion Dollar Mistake. 托尼·霍尔:

我称之为我的十亿美元的错误。它是在1965年发明了null引用。当时我正在设计第一个综合引用类型系统的面向对象语言(ALGOL W)。我的目标是确保所有引用的使用都绝对安全,由编译器自动执行检查。但我忍不住诱惑,加入了一个null引用,因为它非常容易实现。这导致了无数的错误、漏洞和系统崩溃,在过去四十年里可能已经造成了十亿美元的痛苦和损失。近年来,像Microsoft的PREfix和PREfast这样的程序分析器已被用于检查引用,并在存在风险的情况下发出警告。更近期的编程语言,如Spec#,已经引入了非null引用的声明。这正是我在1965年放弃的解决方案。


1
如果能够提出一种非空标识符就好了。在C#中,他们最近添加了可为空的类型,你可以声明为MyType? ...如果能够标识方法参数永远不会为空就好了...比如像MyType!或者其他什么的... - mezoid
15
没有空指针将是一个价值100亿美元的错误。想一想:所有指针都必须初始化为某个“有效”的值,当遇到这个值时,程序会高高兴兴地继续计算在错误的上下文中,导致极难发现的错误=比崩溃更糟糕。 - Steven A. Lowe
10
@Steven A. Lowe:那并不完全正确。看一下 Haskell 中的 Maybe 类型,它像一个空值,但明确表示一个特定的变量可能包含或不包含一个值,这通常导致更好的设计,并且应该很容易在编译器进行优化时去除。 - Daniel O
2
尊重何博士,但我认为这是傲慢自大。;-) 机器语言程序员第一次将指针设置为零时发明了空引用。 - Steven A. Lowe
1
我很好奇Hoare博士会将未指定指针的默认值设置为什么。虽然将指针默认为陷阱表示是可能的(在这种情况下,甚至禁止读取未初始化的指针以查看是否已写入),但是拥有一个空指针值直到实际使用它才会导致错误,在许多情况下似乎更加实用。在许多情况下,分配一个可以检查但无法使用的指针值的能力当然非常有用;在我看来肯定不是错误。 - supercat
显示剩余7条评论

31

null 是一个特殊的值,它既不是整数、字符串、也不是布尔型 - 实际上除了作为“不存在”的值之外,没有其他的用处。请不要将其视为0、空字符串或空列表。这些都是有效的值,在许多情况下可以是真正的有效值 - 相反,null 的概念意味着没有值存在。

或许这有点像函数抛出异常而不是返回值。但与其返回一个具有特殊含义的普通值不同,它返回一个已经具有特殊含义的特殊值。如果一种语言希望你使用 null,那么你就不能忽略它。


好的回答不依赖于值为零,因为它不必是零。你可以补充说,出于性能考虑,编译器通常会使用值0作为空指针值,因为测试是否为0(或非0)通常只需要一条机器指令。 - Robert Paulson
不要将其等同于0、空字符串或空列表。 但也不要过分强调它们的不同。虽然从技术上讲你可能是对的,但避免微妙和令人困惑的细节更加安全。我有点喜欢Oracle将空字符串视为空值的方式。 - Thilo
1
我更喜欢将null和""区分开来。如果我正在检查null,通常不希望""匹配。那个Oracle陷阱非常危险。 - staticsan
@statiscan 我不知道有哪种语言将 null 等同于空字符串 ""。 - Neil N
MySQL在某些情况下会将null转换为“”。 - staticsan

27

哦不,我感觉到了哲学专业的气息...

NULL的概念来自于集合论中空集的概念。几乎所有人都认为空集不等于零。数学家和哲学家们已经争论集合论的价值数十年了。

在编程语言中,我认为了解不指向内存中任何东西的对象引用非常有帮助。搜索关于集合论的文章,你会发现集合论使用的形式符号系统(符号表示法)与许多计算机语言中使用的符号之间存在相似之处。

问候, Sam


1
这似乎是对于是否允许空列表的问题更好的答案,而不是关于是否有一个空值的问题。 - recursive
1
你内心深处有哲学专业?这是怎么回事!? - Rob
1
实际上,自从弗雷格以来,零通常被定义为空集,尽管这对计算机并不重要。(弗雷格将其定义为包含空集的集合。更大数字的定义变化较少。) - Flash Sheridan
我不知道这个在哲学层面上是否有意义,但如果在理论/实践层面上有意义,你需要更好地解释。比如在Java中,null与空集是不同的,当emptySet.size()返回0时,null.size()会抛出空指针异常。 - dainichi
我不确定我需要解释得更好。我承认,我在关于null概念起源的评论中加入了一条注释。接着,我就为什么null在编程语言中有用给出了我的观点。 - codewise
显示剩余2条评论

14

你问什么是null?

嗯,

什么都不是。


12

我通常将 C/C++ 中的“null”看作是“内存地址0”的概念。虽然它不是必需的,但如果它不存在,那么人们就会使用其他东西(例如:if myNumber == -1,或者 if myString ==“”)。

我所知道的是,在编写代码时,我几乎没有一天没有输入过“null”这个单词,因此我认为它非常重要。

在 .NET 世界中,微软最近为 int、long 等类型添加了可空类型,这些类型以前都是不可空的,所以我认为他们也认为它很重要。

如果我设计一种语言,我会保留它。但是,如果一门语言没有 null,我也不会避免使用它。只是需要适应一下。


我也会保留它,确实。我的意思是,你还能给一个指向空的引用分配什么呢?(注意双关语) - Johannes Schaub - litb
这是一个基于错误假设的问题。如果您需要分配一个指向空值的引用,那么您构建应用程序的方式是错误的。 - Breton
Brenton:那么你如何从一个类中实现回调呢?你必须在类中存储一个指向回调函数的指针,但在调用setCallback()之前它会有什么值呢? - Timmmm
可空类型被添加到.NET中,因为它们对于与应用程序或其他天生支持它的语言(如数据库)进行交互是必要的。 - Evan Plaice
2
"null"是一种hack。看看如何使用类型化的函数式语言中的“option”或“Maybe”类型以更加原则性的方式实现相同的功能。 - Kannan Goundan

7

在严格意义上,空值的概念并不像零值那样是必须的。


1
空值是什么运算符的身份识别? - Norman Ramsey
@[Norman Ramsey]:该问题假设了一个无效的前提。Null不是序列中的元素,它是一个表示“没有值”的哨兵。 - Steven A. Lowe
@Norman:虽然我同意null不像零一样基本,但是许多语言都有一个运算符,其中null是恒等元素:http://en.wikipedia.org/wiki/Null_coalescing_operator - Kannan Goundan

7
我认为在整个语言设计的上下文之外讨论null并不有用。第一个困惑点:null类型是空的,还是包含单个的、特殊的值(通常称为“nil”)?完全空的类型并不非常有用---虽然C使用空返回类型void来标记仅用于副作用执行的过程,但许多其他语言使用单例类型(通常为空元组)来实现这一目的。
我发现在动态类型的语言中,nil值被最有效地使用。在Smalltalk中,当需要值但没有任何信息时就使用它。在Lua中,它的使用更加有效:nil值是Lua表中不能成为键或值的唯一值。在Lua中,nil还用作缺少参数或结果的值。
总的来说,nil值在动态类型设置中可以很有用,但在静态类型设置中,null类型只对执行副作用的函数(或过程或方法)进行讨论时有用。
不要使用C和Java中的NULL指针。这些是指针和对象实现中固有的产物,在设计良好的语言中不应该允许它们存在。一定要给你的用户一种使用null值扩展现有类型的方式,但是让他们明确地、有意识地这样做---不要强迫每种类型都有一个。 (作为明确使用的示例,我最近在Haskell中实现了Bentley和Sedgewick的Ternary search tree,并且我需要将字符类型扩展一个额外的值,表示“不是字符”。出于这个目的,Haskell提供了Maybe类型。)
最后,如果你正在编写一个编译器,要记住,最容易编译并导致最少错误的语言部分是不存在的部分:-)

当创建一个引用类型的数组时,应该用什么来初始化数组元素?虽然可以定义一种语言,使得在每个元素的构造函数运行之前无法访问整个数组,但在许多情况下,这并不是很好的解决方案;例如,有一个数组Src和一个置换映射Permute,希望创建一个新数组Dest,使得Dest[Permute[i]]=src[i]。如果Dest预先填充为null,那么Permute[]中的错误就不太可能被忽略了。 - supercat
如果编译器坚持在访问整个数组之前能够确定每个元素的值,那么可能会有人认为性能成本会被非空类型的优势所抵消,但是对于每种类型都有一个标准默认值[请注意,持有null的引用类型变量具有有效的,即使该值不标识任何对象]本身就非常有价值。 - supercat

5

似乎有一种方法可以指示一个当前没有指向任何东西的引用或指针,无论你称其为null、nil、None等。即使出于其他原因,也要让人们知道他们即将从链接列表的末尾掉下来。


3
一个链表可以像这样构建:Node->Node->Node->EndNode。 事实上,在一些没有null值的语言中,这就是列表构建的方式。 - Tom Lokhorst

5
在C语言中,NULL是(void*(0)),因此它是一个带有值的类型。但在C++模板中,这种方式无法使用,所以C++将NULL定义为0,并且去掉了类型,变成了一个纯值。
然而,人们发现拥有特定的NULL类型会更好,因此C++委员会决定在C++0x中再次将NULL定义为一种类型。
此外,除了C++之外,几乎所有语言都将NULL作为一种类型或等效的唯一值(不同于0,可能等于它,也可能不等于它,但不是同一个值)。
因此,即使是C++也会将NULL作为一种类型使用,基本上解决了这个问题的讨论,因为现在每个人(几乎都)都有一个NULL类型。
编辑:考虑到Haskell的maybe也是NULL类型的另一种解决方案,但它不容易理解或实现。

5

Null的一个实际例子是当你提出了一个是或否的问题,但没有得到回答时。你不想默认为否,因为在非常重要的情况下,知道问题未被回答可能很重要。


1
反对的观点是,你会返回三个可能的值,一个是“是”,一个是“否”,另一个是“不知道”。但实际上,在编码时,如果 (a == 不知道) 与在获取值后立即使用 (a == null) 并没有区别。除了在直接阅读代码时可能会有一些不同的思考过程。 - Sekhat
人们的假设是导致“null”错误的原因。假设一个引用在代码中没有明确检查或者没有检查引用来源的文档或代码,就不能为“null”是真正的问题——这是一个边缘情况。就像负长度、距离或年龄一样,这也是一个边缘情况。你需要确保它不会发生(例如,在构造函数中禁止它)或者对其进行防御性编程。 - john16384

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