【问题标题】:What are the rules for a valid dereferencing of a null pointer?有效取消引用空指针的规则是什么?
【发布时间】:2020-10-02 06:55:44
【问题描述】:
#include <iostream>

struct X
{
    bool isNull() { return this == nullptr; }
    bool isNullConst() const { return this == nullptr; }
};

bool isNull(X& x) { return &x == nullptr; }
bool isNullConst(const X& x) { return &x == nullptr; }

// always false or exception.
bool isNullCopy(X x) { return &x == nullptr; } 

int main()
{
    X* x = nullptr;
    std::cout << x->isNull() << '\n';
    std::cout << (*x).isNull() << '\n';
    std::cout << isNull(*x) << '\n';
    // std::cout << isNull2(*x) << '\n'; // exception.
}

在这里,我知道X::isNull() 等价于isNull(X&amp;),而X::isNullConst() 等价于isNullConst(const X&amp;)

我不知道的是,取消引用空指针是正常的。我认为对空指针的任何取消引用都会导致异常。

在玩了一会儿指针之后,我得出结论,取消引用空指针本身不是问题,问题在于尝试读取或写入空指针指向的地址。

由于函数位于内存中众所周知的位置,取消对类的空指针的引用并调用其中一个函数只会导致以空对象作为第一个参数调用函数。

这对我来说是新的,但这可能不是完整的画面。

起初我以为这是一个OOP概念,因此它应该在java中工作,但它在这里不起作用并导致异常(这让我想到为什么它在java中不起作用? ...):

class X
{
    boolean isNull() { return this == null; }
}

public class Main {

    public static void main(String[] args) {
        X x = null;
        System.out.println(x.isNull());
    }
}

所以,显然这是与 C++ 相关的东西,而不是一般的 OOP。

在哪些情况下解除引用空指针有效且不会导致异常?

除了结构和类的指针之外,还有什么其他东西可以成功解引用,即使它们是空指针?

另外,为什么在不访问其字段的情况下调用空指针的函数会在其他语言(如 java)中引发异常?

例如,在红黑树中取消引用空指针是有意义的。空指针被认为是黑色的。

#define RED true
#define BLACK false;

struct Node
{
    bool color;
    
    bool isRed()
    {
        return this != nullptr && this->color == RED;
    }
};

bool isRed(Node* node)
{
    return node != nullptr && node->color == RED;
}

在这里,我认为将函数包含在Node 类本身中更有意义,因为它与它相关。将与节点相关的所有逻辑都包含在其中不是很方便,除了检查它是否为空的逻辑。

【问题讨论】:

  • 首先,Java 和 C++ 是两种不同的语言。第二个 C++ 没有检查非法指针访问。第三,C++ 有一些 Java 中不存在的东西——未定义的行为。
  • 在java中null对象没有任何方法
  • @PaulMcKenzie afaik 它以任何语言存在。 Java 只是尽力隐藏它,而 C++ 编译器则尽力利用它
  • 这些 Java 中的“未定义行为”场景,大部分情况下,不是 JVM 中的错误,而不是与语言相关的问题吗?
  • fwiw,您的编辑添加了一些全新的内容。这几乎可以是一个单独的问题,但答案是:this 永远不是 NULL

标签: c++ oop pointers exception


【解决方案1】:

我认为对空指针的任何取消引用都会导致异常。

没有。在 C++ 中解引用空指针是 undefinded behavior

C++ 不是 Java。 C++ 确实有例外,但它们仅用于例外情况,而不是到处使用(如在 Java 中)。您应该知道不允许取消引用空指针,并且编译器假定它永远不会在正确的代码中发生。如果仍然发生,您的代码无效。

了解未定义的行为。当你想在 C++ 中做任何严肃的事情时,了解它是必不可少的。

空指针的有效解引用的规则是什么?

规则是:你不能这样做。当你这样做时,你的代码格式不正确,不需要诊断。这是另一种说法:您的代码具有未定义的行为。编译器不需要发出错误或警告,当你要求编译器编译你的错误代码时,结果可以是任何东西。

【讨论】:

  • 在尝试寻找一些 UB 时写这个:/
  • 我已经更新了问题并包含了一个示例,其中取消引用空指针是有意义的。
  • @StackExchange123 我看到了例子,但坦率地说,它没有意义
  • @StackExchange123 您所说的主要是实现细节。这不是 C++ 的工作方式。在 C++ 中无法在空指针上调用函数。没有争论会改变任何事情
  • @StackExchange123 See this。编译器一旦发现您使用的是空指针,就会生成不期望的代码。例如,编译器可能假定this 永远不可能是null,因此比较返回false,而不是true
【解决方案2】:

在 Java 中,您的对象声明是引用。因此,您可以将空引用传递给方法,并且不会造成损害,因为该方法可以检查引用是否指向空对象。

但是在空引用上调用方法是行不通的,因为该方法是在引用后面的对象上调用的。由于它为 null,因此无法在任何对象上调用该方法,因此会引发 NullpointerException。

【讨论】:

    【解决方案3】:

    [在 C++ 中] 有效取消引用空指针的规则是什么?

    C++ 标准实际上对通过空指针进行间接寻址本身是否有效有些不明确。它没有被明确禁止。该标准曾经使用“取消引用空指针”作为未定义行为的示例,但此示例已被删除。

    有一个活跃的核心语言问题CWG-232,标题为“是否通过空指针进行间接未定义行为?”对此进行了讨论。它建议更改措辞以明确允许通过空指针进行间接引用,甚至允许语言中的“空”引用。该问题是 20 年前创建的,最近一次更新是在 15 年前,当时发现建议的措辞不够充分。


    这里有几个例子:

    X* ptr = nullptr;
    
    *ptr;
    

    上面,间接的结果被丢弃了。这是标准没有明确说明其有效性的一种情况。拟议的措辞将明确允许这一点。这也是一个相当没有意义的操作。

    X& x = *ptr;
    X* ptr2 = &x; // ptr2 == nullptr?
    

    上面,通过 null 的间接结果绑定到一个左值。现在这是明确未定义的行为,但建议的措辞允许这样做。

    ptr->member_function();
    

    上面,间接的结果经过左值到右值的转换。无论函数做什么,它都有未定义的行为,并且在 CWG-232 提议的决议中将保持未定义。这同样适用于您的所有示例。

    这样做的一个结果是return this == nullptr; 可以优化为return false;,因为this 在定义良好的程序中永远不能为空。

    【讨论】:

      【解决方案4】:

      在 C++ 中取消引用 nullptr 是一种未定义的行为,因此从技术上讲,当您尝试取消引用 nullptr 时,任何事情都可能发生(我的意思是:任何事情 em> :))。

      【讨论】:

        猜你喜欢
        • 2014-03-03
        • 2014-01-17
        • 1970-01-01
        • 2017-09-28
        • 1970-01-01
        • 2011-10-11
        • 1970-01-01
        • 2013-02-20
        相关资源
        最近更新 更多