null的目的是什么?

时间:2009-02-25 02:30:36

标签: language-agnostic compiler-construction null language-design

我在编译器类中,我们的任务是从头开始创建自己的语言。目前我们的困境是是否包含'null'类型。 null提供了什么目的?我们的一些团队认为这不是绝对必要的,而其他人只是因为它可以提供额外的灵活性而无效。

你有什么想法,特别是赞成或反对null吗? 您是否曾创建过需要null的功能?

25 个答案:

答案 0 :(得分:42)

Null: The Billion Dollar Mistake。 Tony Hoare:

  

我称之为十亿美元的错误。   这是null的发明   在1965年参考。当时,我是   设计第一个综合类型   用于对象中引用的系统   面向语言(ALGOL W)。我的目标   是为了确保所有的使用   引用应绝对安全,   检查自动执行   由编译器。 但我无法抗拒   投入无效的诱惑   参考,仅仅因为它是如此   易于实施。这导致了   无数错误,漏洞,   和系统崩溃,有   可能造成了十亿美元的损失   最后四十年的痛苦和伤害   年份。近年来,一些   程序分析器,如PREfix和   微软的PREfast已经习惯了   检查参考,并给出警告   存在风险,它们可能是非空的。   最近的编程语言如   Spec#引入了声明   非空引用。这是   解决方案,我在1965年拒绝了。

答案 1 :(得分:28)

null是一个不是整数的标记值,不是字符串,不是布尔值 - 实际上没有任何东西,除了要保留的东西并且是“不存在”值。不要将其视为或期望它为0,或空字符串或空列表。这些都是有效的值,并且在许多情况下可以是geniunely有效值 - 而null的概念意味着那里有 no 值。

也许它有点像抛出异常而不是返回值的函数。除了制造和返回具有特殊含义的普通值之外,它返回一个已经具有特殊含义的特殊值。如果某种语言希望您使用null,那么您就无法忽略它。

答案 2 :(得分:26)

哦,不,我觉得哲学专业来自我......

NULL的概念来自集合论中空集的概念。几乎每个人都同意空集不等于零。几十年来,数学家和哲学家一直在争论集合论的价值。

在编程语言中,我认为理解不引用内存中任何内容的对象引用非常有用。谷歌关于集合理论,你会看到设置理论家使用的正式符号系统(符号)和我们在许多计算机语言中使用的符号之间的相似之处。

此致 萨姆

答案 3 :(得分:14)

你问什么是空的?

那么,

没有

答案 4 :(得分:12)

我通常认为'内存地址0'的C / C ++方面的'null'。它不是严格需要的,但如果它不存在,那么人们只会使用别的东西(如果myNumber == -1,或者myString ==“”)。

我所知道的是,我想不出有一天我花了编码而没有输入“null”这个词,所以我认为这非常重要。

在.NET世界中,MS最近为int,long等添加了可以为空的类型的可空类型,所以我猜他们认为它非常重要。

如果我正在设计一个语言,我会保留它。但是,我不会避免使用没有null的语言。它也只需要一点点使用。

答案 5 :(得分:7)

零的概念并不是完全相同的意义,零概念并非绝对必要。

答案 6 :(得分:7)

我不认为在整个语言设计的上下文之外讨论null是有帮助的。混淆的第一点:null类型是空的,还是包含一个唯一的值(通常称为“nil”)?完全空的类型不是很有用 - 虽然C使用空返回类型void来标记仅为副作用执行的过程,但许多其他语言使用单例类型(通常是空元组)目的

我发现在动态类型语言中最有效地使用了nil值。在Smalltalk中,它是您需要值但您没有任何信息时使用的值。在Lua中,它的使用效率更高:nil值是唯一不能成为Lua表中的键或值的值。在Lua中,nil也用作缺失参数或结果的值。

总的来说,我会说nil 在动态类型设置中很有用,但在静态类型设置中,null 类型仅用于讨论为副作用执行的函数(或过程或方法)。

不惜一切代价,避免使用C和Java中使用的NULL 指针。这些是指针和对象实现中固有的工件,并且在设计良好的语言中,它们不应被允许。通过任何方式为您的用户提供一种方法来扩展一个具有空值的现有类型,但是让他们明确地明确地这样做 - 不要强迫每个类型偶然拥有一个类型。 (作为明确使用的一个例子,我最近在Haskell中实现了Bentley和Sedgewick的三元搜索树,我需要使用一个额外的值来扩展字符类型,这意味着“不是一个字符”。为此,Haskell提供Maybe类型。)

最后,如果您正在编写编译器,最好记住要编译的语言最简单的部分,以及导致最少错误的部分,是不存在的部分: - )

答案 7 :(得分:5)

在C NULL中是(void *(0)),所以它是一个带值(?)的类型。但这对C ++模板不起作用,因此C ++使NULL为0,它删除了类型并成为纯值。

然而,发现具有特定的NULL类型会更好,所以他们(C ++委员会)决定NULL将再次成为一个类型(在C ++ 0x中)。

除了C ++之外,几乎所有语言都有NULL作为类型,或者等价的唯一值与0不同(它可能等于或不等,但它的值不同)。

所以现在即使C ++也会使用NULL作为一种类型,基本上关闭了关于这个问题的讨论,因为现在每个人(几乎)都会有一个NULL类型

编辑:考虑一下Haskell可能是NULL类型的另一种解决方案,但它不容易掌握或实现。

答案 8 :(得分:5)

有一种方法可以指示当前没有指向任何内容的引用或指针,无论你将其称为null,nil,None等。如​​果没有其他理由让人们知道它们何时出现即将脱离链表的末尾。

答案 9 :(得分:3)

Null不是一个错误。 Null的意思是“我还不知道”

对于原语,你真的不需要null(我不得不说字符串(在.NET中)不应该得到它IMHO)

但对于复合实体来说,它绝对有用。

答案 10 :(得分:3)

例如,考虑C和Java的示例。在C中,约定是空指针是数值零。当然,这只是一个惯例:没有任何关于语言将这个价值视为特殊的东西。然而,在Java中,null是一个独特的概念,您可以检测并知道,是的,这实际上是一个不好的参考,我不应该试图打开那扇门看看另一边是什么。 / p>

即便如此,我讨厌的空虚几乎比其他任何事情都要糟糕。

澄清基于评论:我讨厌比我讨厌null更糟糕的事实上的空指针值。

每当我看到null的赋值时,我想,“哦,好吧,有人刚刚在代码中添加了一个地雷。总有一天,我们将走在相关的执行路径上并且 BOOM !NullPointerException!“

我更喜欢的是某人指定一个有用的默认值或NullObject,让我知道“此参数尚未设置为任何有用的参数”。光头零本身就是等待发生的麻烦。

那就是说,它仍然比散落的原始零点更好。

答案 11 :(得分:3)

Null仅在存在具有未分配值的变量的情况下才有用。如果每个变量都有一个值,那么就不需要空值。

答案 12 :(得分:3)

您可以将任何类型视为一组以及一组操作。在许多情况下,使用不是“正常”值的值很方便;例如,考虑“EOF”值。对于C getline()。您可以通过以下几种方式之一处理:您可以在集合外部使用NULL值,您可以将特定值区分为null(在C中,((void *)0)可以用于此目的)或者您可以创建一种方法一个新类型,所以对于类型 T ,你创建一个类型 T' = def {T∪NULL} ,这就是Haskell的方式它(“可能”类型)。

哪一个更好是有益于许多令人愉快的论点。

答案 13 :(得分:3)

Null是一个哨兵值。它是一个不可能是真实数据的值,而是提供有关正在使用的变量的元数据。

指定给指针的空指示指针未初始化。这使您能够通过检测空值指针的解引用来检测未初始化指针的滥用。如果您改为将指针的值保留在内存中,那么您将会遇到疯狂的不规则程序行为,这将很难调试。

此外,C风格的可变长度字符串中的空字符用于标记字符串的结尾。

以这些方式使用null,特别是对于指针值,已经变得如此受欢迎,以至于隐喻已被导入到其他系统中,即使“null”sentinel值完全不同地实现并且与数字0无关

答案 14 :(得分:3)

null的一个实际例子是当你提出是/否问题并且没有得到回复时。您不希望默认为no,因为在答案非常重要的情况下,知道问题没有得到解答可能很重要。

答案 15 :(得分:3)

Null不是问题 - 每个人都在处理和解释null不同的问题。

我喜欢null。如果没有null,则只能用其他方式替换null,以便代码说“我没有线索,伙计!” (有些人会写“我不知道,男人!”或“我没有线索,老豆!”等等,我们再次遇到完全相同的问题。)

我概括,我知道。

答案 16 :(得分:2)

该决定取决于编程语言的目标。

您是为谁设计编程语言?您是否为熟悉c-derived语言的人设计它?如果是这样,那么你应该添加对null的支持。

一般来说,我会说你应该避免违反人们的期望,除非它有特定目的。

以C#中的switch-block为例。 C#中的所有案例标签必须在每个分支中都有一个显式的控制流表达式。也就是说,他们必须以“休息”声明或明确的转到结尾。这意味着虽然这段代码是合法的:

switch(x)
{
    case 1:
    case 2:
        foo;
        break;
}

这段代码不合法​​:

switch (x)
{
    case 1:
        foo();
    case 2:
        bar();
        break;
}

为了从案例1到案例2创建“堕落”,需要插入一个goto,如下所示:

switch (x)
{
    case 1:
        foo();
        goto case 2;
    case 2:
        bar();
        break;
}

这可以说是违反了C#程序员倾向于C#的期望。但是,添加该限制有助于达到目的。它消除了一整类常见C ++错误的可能性。它略微增加了语言的学习曲线,但结果对程序员来说是一个净利益。

如果您的目标是设计针对C ++程序员的语言,那么删除null可能会违反他们的期望。这将导致混乱,并使您的语言更难学习。关键问题是,“他们得到了什么好处”?或者,或者“这会造成什么损害”。

如果你只是想设计一个可以在一个学期内实施的“超小语言”,那么故事就不同了。在这种情况下,您的目标不是构建针对特定人群的有用语言。相反,它只是学习如何创建编译器。在这种情况下,使用较小的语言是一个很大的好处,因此值得消除null。

所以,回顾一下,我会说你应该:

  1. 确定您创建语言的目标。谁是为其设计的语言,以及他们的需求。
  2. 根据目标用户以最佳方式实现目标的方式做出决定。
  3. 通常这会使期望的结果非常清晰。

    当然,如果你没有明确表达你的设计目标,或者你不能就它们是什么达成一致,那么你仍然会争论。然而,在这种情况下,你无论如何都要注定失败。

答案 17 :(得分:2)

  

null提供了什么目的?

我相信这里有两个null概念。

第一个(逻辑指示符为空)是一种传统的程序语言机制,它在程序逻辑中提供非初始化内存引用的运行时指示。

第二个(值为null)是一个基本数据值,可以在逻辑表达式中用于检测逻辑空指示符(前一个定义)并在程序代码中做出逻辑决策。

  

你有什么想法,特别是支持或反对null?

虽然null多年来一直是许多程序员的祸根和许多应用程序错误的根源,但null概念具有有效性。如果您和您的团队创建的语言使用可能被滥用的内存引用,因为引用未初始化,您可能需要一种机制来检测该可能性。创建替代方案始终是一种选择,但null是众所周知的替代方案。

总而言之,这完全取决于您的语言目标:

  1. 目标节目受众
  2. 鲁棒性
  3. 性能
  4. 等...
  5. 如果优先级列表中的健壮性和程序正确性很高并且允许编程内存引用,则需要考虑null。

    BB

答案 18 :(得分:2)

另一种看待null的方法是它是一个性能问题。如果你有一个包含其他复杂对象的复杂对象等等,那么允许所有属性最初变为null而不是创建某种空的对象更有效,这些空对象不会有任何好处并且很快就会被替换。

这只是我以前看不到的一个视角。

答案 19 :(得分:1)

使用空对象模式!

如果您的语言是面向对象的,那么让它有一个UndefinedValue类,其中只存在一个单例实例。然后在使用null的任何地方使用此实例。这样做的好处是,您的null会响应#toString#equals等消息。您将永远不会像在Java中那样遇到空指针异常。 (当然,这要求您的语言是动态输入的。)

答案 20 :(得分:1)

我对您的团队的建议是:提出一些需要用您的语言编写的示例程序,并了解如果您遗漏null,看看它们看起来如何,而不是包含它。

答案 21 :(得分:1)

Null是一个占位符,表示没有值(为静态类型语言附加“正确类型”)可以分配给该变量。

这里存在认知失调。我在其他地方听说人类无法理解否定,因为他们必须设想一个价值,然后想象它的不合适。

答案 22 :(得分:1)

如果要创建静态类型语言,我认为null可能会给编译器增加很多复杂性。

如果你正在创建一个动态类型的语言,NULL可以派上用场,因为它只是另一个没有任何变化的“类型”。

答案 23 :(得分:0)

Null是对象0到数字的对象。

答案 24 :(得分:0)

Null为程序员提供了一个简单的出路,他们没有完全考虑他们的程序所需的逻辑和域,或者使用基本没有明确和一致定义的值的未来维护含义。

起初看起来似乎很明显它必须意味着“没有价值”,但实际上意味着什么取决于背景。如果,例如LastName === null,这是否意味着该人没有姓氏,或者我们不知道他们的姓氏是什么,或者它还没有输入系统? null是否等于自己,或者不是吗?在SQL中它没有。在许多语言中它确实如此。但是如果我们不知道personA.lastName或personB.lastName的值,我们怎么知道personA.lastName === personB.lastName,是吗?结果应该是假的,还是......空值?

这取决于你正在做什么,这就是为什么拥有某种系统范围的价值是危险和愚蠢的,可以用于任何类似“什么都没有”的情况,因为其他部分如何您的程序和外部库或模块无法真正依赖于正确解释“null”的含义。

你最好明确定义lastName的可能值的DOMAIN,以及每个可能的值实际意味着什么,而不是依赖于一些模糊的系统范围的null概念,这可能与你的任何相关或不相关正在做,取决于你正在使用的语言,以及你正在尝试做什么。实际上,当您开始对数据进行操作时,该值可能会以完全错误的方式运行。