首页 文章

什么是nullptr?

提问于
浏览
506

我们现在拥有许多新功能的C 11 . 一个有趣且令人困惑的(至少对我而言)是新的 nullptr .

嗯,不再需要令人讨厌的宏 NULL .

int* x = nullptr;
myclass* obj = nullptr;

不过,我还没有得到 nullptr 的工作方式 . 例如,Wikipedia article说:

C 11通过引入一个新关键字作为区分空指针常量来解决这个问题:nullptr . 它的类型为nullptr_t,它是可隐式转换的,可与任何指针类型或指向成员类型的类型相媲美 . 除了bool之外,它不可隐式转换或与整数类型相比 .

它是一个关键字和一个类型的实例?

另外,你有另一个例子(在维基百科旁边),其中 nullptr 优于好老 0

11 回答

  • 366

    好吧,其他语言保留了类型实例的单词 . 例如,Python:

    >>> None = 5
      File "<stdin>", line 1
    SyntaxError: assignment to None
    >>> type(None)
    <type 'NoneType'>
    

    这实际上是一个相当接近的比较,因为 None 通常用于尚未初始化的东西,但同时比较如 None == 0 是假的 .

    另一方面,在普通C中, NULL == 0 将返回真正的IIRC,因为 NULL 只是一个返回0的宏,它总是一个无效的地址(AFAIK) .

  • 52

    它是一个关键字和一个类型的实例?

    这并不奇怪 . truefalse 都是关键字,而文字则是一个类型( bool ) . nullptrstd::nullptr_t 类型的指针文字,它是一个prvalue(你不能使用 & 获取它的地址) .

    关于指针转换的

    • 4.10 表示 std::nullptr_t 类型的prvalue是一个空指针常量,并且一个整数空指针常量可以转换为 std::nullptr_t . 不允许相反的方向 . 这允许为指针和整数重载函数,并传递 nullptr 以选择指针版本 . 传递 NULL0 会令人困惑地选择 int 版本 .

    • nullptr_t 到整数类型的强制类型需要 reinterpret_cast ,并且具有与 (void*)0 的强制类型相同的语义到整数类型(映射实现定义) . reinterpret_cast 无法将 nullptr_t 转换为任何指针类型 . 如果可能,请依靠隐式转换或使用 static_cast .

    • 标准要求 sizeof(nullptr_t)sizeof(void*) .

  • 4

    来自nullptr: A Type-safe and Clear-Cut Null Pointer

    新的C 09 nullptr关键字指定一个rvalue常量,用作通用空指针文字,替换错误和弱类型的文字0和臭名昭着的NULL宏 . 因此,nullptr结束了超过30年的尴尬,模棱两可和错误 . 以下部分介绍了nullptr工具,并展示了如何解决NULL和0的问题 .

    其他参考:

  • 3

    当你有一个可以接收指向多个类型的指针的函数时,用 NULL 调用它是不明确的 . 现在解决这个问题的方法是非常hacky接受一个int并假设它是 NULL .

    template <class T>
    class ptr {
        T* p_;
        public:
            ptr(T* p) : p_(p) {}
    
            template <class U>
            ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
    
            // Without this ptr<T> p(NULL) would be ambiguous
            ptr(int null) : p_(NULL)  { assert(null == NULL); }
    };
    

    C++11 中,您将能够在 nullptr_t 上重载,以便 ptr<T> p(42); 将是编译时错误而不是运行时 assert .

    ptr(std::nullptr_t) : p_(nullptr)  {  }
    
  • 3

    为什么C 11中的nullptr?它是什么?为什么NULL不够?

    C专家Alex Allain says it perfectly here

    “...想象你有以下两个函数声明:

    void func(int n); 
    void func(char *s);
    
    func( NULL ); // guess which function gets called?
    

    Although it looks like the second function will be called--you are, after all, passing in what seems to be a pointer--it's really the first function that will be called! The trouble is that because NULL is 0, and 0 is an integer, the first version of func will be called instead. 这是一种事情,是的,不知道发生了什么的细节,它可能看起来像编译器错误 . A language feature that looks like a compiler bug is, well, not something you want.

    Enter nullptr. In C++11, nullptr is a new keyword that can (and should!) be used to represent NULL pointers; 换句话说,无论你以前写过NULL,都应该使用nullptr . It's no more clear to you, the programmer ,(每个人都知道NULL意味着什么), but it's more explicit to the compiler ,当用作指针时,它将不再看到任何地方被用来具有特殊含义 .

  • 0

    nullptr 不能分配给 integral type ,例如int,但只能分配 pointer ;内置指针类型(如 int *ptr )或智能指针(如 std::shared_ptr<T>

    我相信这是一个重要的区别,因为 NULL 仍然可以分配给 integral typepointer ,因为 NULL 是一个扩展为 0 的宏,它既可以作为 int 的初始值,也可以作为 pointer .

  • 34

    另外,你有另一个例子(在维基百科旁边),其中nullptr优于好老0吗?

    是 . 它也是我们的 生产环境 代码中出现的(简化的)现实世界的例子 . 它只是突出,因为gcc能够在交叉编译到具有不同寄存器宽度的平台时发出警告(仍然不确定为什么只有当从x86_64交叉编译到x86时,警告 warning: converting to non-pointer type 'int' from NULL ):

    考虑这段代码(C 03):

    #include <iostream>
    
    struct B {};
    
    struct A
    {
        operator B*() {return 0;}
        operator bool() {return true;}
    };
    
    int main()
    {
        A a;
        B* pb = 0;
        typedef void* null_ptr_t;
        null_ptr_t null = 0;
    
        std::cout << "(a == pb): " << (a == pb) << std::endl;
        std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning
        std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes
        std::cout << "(a == null): " << (a == null) << std::endl;
    }
    

    它产生这个输出:

    (a == pb): 1
    (a == 0): 0
    (a == NULL): 0
    (a == null): 1
    
  • 15

    它是一个关键字,因为标准将指定它 . ;-)根据最新消息公共草案(n2914)

    2.14.7指针文字[lex.nullptr]指针 - 文字:
    nullptr
    指针文字是关键字nullptr . 它是std :: nullptr_t类型的右值 .

    它很有用,因为它不会隐式转换为整数值 .

  • -2

    假设你有一个函数(f),它被重载以获取int和char * . 在C 11之前,如果你想用空指针调用它,并且你使用NULL(即值0),那么你将调用为int重载的那个:

    void f(int);
    void f(char*);
    
    void g() 
    {
      f(0); // Calls f(int).
      f(NULL); // Equals to f(0). Calls f(int).
    }
    

    这可能不是你想要的 . C 11用nullptr解决了这个问题;现在您可以编写以下内容:

    void g()
    {
      f(nullptr); //calls f(char*)
    }
    
  • 8

    0曾经是唯一可以用作指针的无转换初始化器的整数值:你不能在没有强制转换的情况下用其他整数值初始化指针 . 您可以将0视为consexpr singleton,在语法上类似于整数文字 . 它可以启动任何指针或整数 . 但令人惊讶的是,你会发现它没有明显的类型:它是 int . 那么为什么0可以初始化指针而1不能?一个实际的答案是我们需要一种定义指针空值的方法,并且 int 直接隐式转换为指针容易出错 . 因此,在史前时代,0变成了一个真正怪异的怪兽 . nullptr 被提议为初始化指针的空值的真实单例constexpr表示 . 它不能用于直接初始化整数并消除以0表示定义 NULL 所涉及的歧义 . 可以使用std语法将_1772389定义为库,但在语义上看起来是缺少的核心组件 . NULL 现已弃用,而不是 nullptr ,除非某些库决定将其定义为 nullptr .

  • 1

    NULL不必为0.只要使用始终为NULL且永远不为0,NULL可以是任何值 . 假设您使用平坦内存对von Neuman微控制器进行编程,其中断vektors为0.如果NULL为0且某些内容写入NULL指针,则微控制器崩溃 . 如果允许NULL为1024而在1024处有一个保留变量,则写入不会使其崩溃,您可以从程序内部检测NULL指针赋值 . 这在PC上是无意义的,但对于太空探测器,军事或医疗设备而言,重要的是不要崩溃 .

相关问题