为什么 C++ 的 NULL 是整数文字而不是像 C 中的指针?

ein*_*ica 9 c c++ cross-language null-pointer nullptr

我已经编写 C++ 多年,nullptr用于空指针。我也知道 C,从那里 NULL 起源,并记住它是一个空指针的常量,类型void *

出于NULL某些原因,我不得不在我的 C++ 代码中使用某些东西。好吧,想象一下当在一些模板参数推导过程中编译器告诉我我的 NULL 真的是一个......长时我的惊讶。所以,我仔细检查了:

#include <type_traits>
#include <cstddef>

static_assert(not std::is_same<decltype(NULL), long>::value, "NULL is long ???");
Run Code Online (Sandbox Code Playgroud)

事实上,静态断言失败(使用 GCC 和 clang)。

cppreference.com 上检查,果然(C++11 措辞):

宏 NULL 是一个实现定义的空指针常量,它可以是一个值为 0 的整数文字,或者一个类型为 的纯右值std::nullptr_t

为什么这是有道理的?就其本身而言,鉴于 C 的不兼容性?

Nic*_*las 14

在 C 中, avoid*可以隐式转换为 any T*。因此,制作NULL一个void*是完全合适的。

但这非常危险。因此,C++ 取消了这种转换,要求您手动执行大多数指针转换。但这会导致与 C 的源代码不兼容;使用NULLC 想要的方式的有效 C 程序将无法在 C++ 中编译。它还需要一堆冗余:T *pt = (T*)(NULL);,这将是令人讨厌和毫无意义的。

所以 C++ 将NULL宏重新定义为整型字面量 0。在 C 中,字面量 0 也可以隐式转换为任何指针类型并生成空指针值,C++ 保留了这种行为。

当然,现在对空指针常量使用文字 0(或更准确地说,值为 0 的整数常量表达式)不是最好的主意。特别是在允许重载的语言中。因此,C++11 在一个关键字上完全使用 NULL,该关键字专门表示“空指针常量”,仅此而已。

  • “文字 0 充当空指针常量”是 C++ 的创新吗?像 [C 常见问题解答](http://c-faq.com/null/null2.html) 引用 K&amp;R 说这也发生在 C 中。 (3认同)
  • @PeterCordes 在“早期”K&amp;R C 中,您不需要任何强制转换来从整数转换为指针并返回(并且“void”根本不是关键字),因此强制转换“0”是毫无意义的`。像“#define FOO 0170200”之类的东西,后面跟着“struct foo { int bar;” }; ...; FOO-&gt;bar = 17` 完全有效并在各处使用。FWIW,Unix 版本 7 源代码有“#define NULL 0”。 (3认同)
  • @Peter,K&amp;R 第一版没有“void”,并将 NULL 定义为 0。(在示例代码中,第 97 页)。 (2认同)

Tob*_*ght 12

这两种语言中,NULL实现定义的空指针常量。C++第17.2.3节,其定义参考C 7.19。

也就是说,在C中,NULL可以定义为整数类型或者为类型void*,而在C++中可以定义为整数类型;nullptr_t如果我正确解释了标准,它也可以被定义为 a 。隐藏的(非规范的)脚注表明这(void*)0不可能是有效的定义,这是有道理的,因为 C++ 没有 Cvoid*到其他指针类型的隐式转换。

如果您有一个将其定义为 的 C 编译器((void*)0)和一个将其定义为 的 C++ 编译器0L,则它们都是一致的。两种实现都满足用例(可以从 分配并与 进行比较NULL)。

C++ 的引入nullptr主要是为了提供适合重载的类型值,因为传递NULL给函数可以选择整数版本被认为是令人惊讶的。

  • 1.它反驳了问题的前提 - 两个规范都说它是实现定义的,并且它必须是一个空指针常量。2. C++ 标准准确地说“[宏 `NULL` 是实现定义的空指针常量。另请参阅:ISO C 7.19](https://eel.is/c++draft/support.types.nullptr) “我不明白你怎么能比这更有权威。 (6认同)
  • @einpoklum 0 和 0L 都是 C++ 中的空指针常量(没有引号),因为标准是这么说的。 (6认同)
  • @einpoklum“空指针常量”是一个术语,指的是(a)常量和(b)在需要指针的上下文中表示空指针的东西。`0L` 是一个 `long` 类型的常量,它绝对不是指针。这并不能阻止它成为空指针常量。如果你不喜欢这个词,你可以尝试想出一个更好的词,也许会有足够多的人喜欢它。如果您不喜欢“0L”能够代表空指针这一事实,那么运气不好,因为向后兼容。 (6认同)
  • @einpoklum:“*说它是一个“空指针常量”只是糟糕的措辞*”我不明白它是否是“糟糕的措辞”有什么关系。该标准对该术语及其使用方式有明确的定义。你可能不喜欢这个词,但它就是这样。 (4认同)
  • 在 C++ 中 `(void*)0` 不是(https://eel.is/c++draft/support#footnoteref-176)空指针常量,因为](https://eel.is/c ++草稿/conv.ptr#1)。 (3认同)

Dav*_*ley 10

C 并不简单地定义NULL(void *)0. 也可以将其定义为0或任何计算结果的常量表达式。使用#define NULL 0任一语言的作品。

编辑: 这个答案很好而且更详细。


Sne*_*tel 6

我的假设始终是,实现的决定保留NULLas0是与 C++98 向后兼容的问题。我见过很多非指针上下文中使用的代码NULL,特别是作为(可能是无意的,或者至少是无知的)数组中空终止符的替代品。由于自 '11 以来的最佳实践是“NULL根本不使用”,因此重新定义NULLtonullptr可能会破坏坏/旧代码,而对好/新代码几乎没有帮助。

  • 呃……在 C++98 中,还能是什么?他们没有远见来创建“nullptr”,也无法使用“(void*)0”,所以他们实际上只有一个选择。(带有可选的“L”以实现良好的测量。) (2认同)