为什么 C++ 的 NULL 通常是整数文字而不是 C 中的指针?
Why is C++'s NULL typically an integer literal rather than a pointer like in C?
多年来我一直在编写 C++,对空指针使用 nullptr
。我也知道 C,NULL 起源于此,请记住它是空指针的常量,类型为 void *
.
出于某些原因,我不得不在我的 C++ 代码中使用 NULL
。好吧,想象一下当在一些模板参数推导过程中编译器告诉我我的 NULL 真的是一个......长时我的惊讶。所以,我再次检查:
#include <type_traits>
#include <cstddef>
static_assert(not std::is_same<decltype(NULL), long>::value, "NULL is long ???");
确实,静态断言失败了(使用 GCC 和 Clang)。
我查看了 cppreference.com,果然(C++11 的措辞):
The macro NULL is an implementation-defined null pointer constant, which may be
an integer literal with value zero, or a prvalue of type std::nullptr_t
.
为什么这是有道理的?就其本身而言,并考虑到 C 的不兼容性?
在 C 中,void*
可以隐式转换为任何 T*
。因此,将 NULL
设为 void*
是完全合适的。
但这非常危险。所以 C++ 取消了此类转换,要求您手动执行大多数指针转换。但这会造成与 C 的源代码不兼容;一个使用 NULL
C 想要的方式的有效 C 程序将无法在 C++ 中编译。它还需要一堆冗余:T *pt = (T*)(NULL);
,这会令人恼火且毫无意义。
因此 C++ 将 NULL
宏重新定义为整数文字 0。在 C 中,the literal 0 is also implicitly convertible to any pointer type 并生成空指针值,这是 C++ 保留的行为。
现在,当然,将文字 0(或更准确地说,值为 0 的整数常量表达式)用于空指针常量……不是最好的主意。特别是在允许重载的语言中。因此,C++11 完全放弃了使用 NULL 的关键字,该关键字专门表示“空指针常量”,仅此而已。
在两种语言中,NULL
是一个实现定义的空指针常量。 C++ 部分 17.2.3,其定义参考了 C 7.19。
表示在C中,NULL
可以定义为整数类型,也可以定义为void*
类型,在C++中可以定义为整数类型;如果我正确解释了标准,它也可以定义为 nullptr_t
。一个隐藏的(非规范的)脚注说 (void*)0
不能是一个有效的定义,这是有道理的,因为 C++ 没有 C 从 void*
到其他指针类型的隐式转换。
如果您有一个将其定义为 ((void*)0)
的 C 编译器和一个将其定义为 0L
的 C++ 编译器,它们都是一致的。两种实现都满足用例(可以从 NULL
分配和比较)。
C++ 引入 nullptr
主要是为了提供适合重载的类型化值,因为将 NULL
传递给函数可以 select 整数版本被认为令人惊讶。
我的假设一直是实现将 NULL
保持为 0
的决定是与 C++98 向后兼容的问题。我已经看到 lot 的代码在非指针上下文中使用 NULL
,特别是作为(可能是无意的,或者至少是不知情的)替代数组中的空终止符.自 11 年以来的最佳实践是“根本不使用 NULL
”,将 NULL
重新定义为 nullptr
可能会破坏 bad/old 代码,而对帮助 good/new 代码。
C 并不简单地将 NULL
定义为 (void *)0
。也可以将其定义为 0
或任何计算结果为它的常量表达式。使用 #define NULL 0
在任何一种语言中都有效。
编辑:this answer非常好,也比较详细。
在
vcruntime.h
定义如下:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
多年来我一直在编写 C++,对空指针使用 nullptr
。我也知道 C,NULL 起源于此,请记住它是空指针的常量,类型为 void *
.
出于某些原因,我不得不在我的 C++ 代码中使用 NULL
。好吧,想象一下当在一些模板参数推导过程中编译器告诉我我的 NULL 真的是一个......长时我的惊讶。所以,我再次检查:
#include <type_traits>
#include <cstddef>
static_assert(not std::is_same<decltype(NULL), long>::value, "NULL is long ???");
确实,静态断言失败了(使用 GCC 和 Clang)。
我查看了 cppreference.com,果然(C++11 的措辞):
The macro NULL is an implementation-defined null pointer constant, which may be an integer literal with value zero, or a prvalue of type
std::nullptr_t
.
为什么这是有道理的?就其本身而言,并考虑到 C 的不兼容性?
在 C 中,void*
可以隐式转换为任何 T*
。因此,将 NULL
设为 void*
是完全合适的。
但这非常危险。所以 C++ 取消了此类转换,要求您手动执行大多数指针转换。但这会造成与 C 的源代码不兼容;一个使用 NULL
C 想要的方式的有效 C 程序将无法在 C++ 中编译。它还需要一堆冗余:T *pt = (T*)(NULL);
,这会令人恼火且毫无意义。
因此 C++ 将 NULL
宏重新定义为整数文字 0。在 C 中,the literal 0 is also implicitly convertible to any pointer type 并生成空指针值,这是 C++ 保留的行为。
现在,当然,将文字 0(或更准确地说,值为 0 的整数常量表达式)用于空指针常量……不是最好的主意。特别是在允许重载的语言中。因此,C++11 完全放弃了使用 NULL 的关键字,该关键字专门表示“空指针常量”,仅此而已。
在两种语言中,NULL
是一个实现定义的空指针常量。 C++ 部分 17.2.3,其定义参考了 C 7.19。
表示在C中,NULL
可以定义为整数类型,也可以定义为void*
类型,在C++中可以定义为整数类型;如果我正确解释了标准,它也可以定义为 nullptr_t
。一个隐藏的(非规范的)脚注说 (void*)0
不能是一个有效的定义,这是有道理的,因为 C++ 没有 C 从 void*
到其他指针类型的隐式转换。
如果您有一个将其定义为 ((void*)0)
的 C 编译器和一个将其定义为 0L
的 C++ 编译器,它们都是一致的。两种实现都满足用例(可以从 NULL
分配和比较)。
C++ 引入 nullptr
主要是为了提供适合重载的类型化值,因为将 NULL
传递给函数可以 select 整数版本被认为令人惊讶。
我的假设一直是实现将 NULL
保持为 0
的决定是与 C++98 向后兼容的问题。我已经看到 lot 的代码在非指针上下文中使用 NULL
,特别是作为(可能是无意的,或者至少是不知情的)替代数组中的空终止符.自 11 年以来的最佳实践是“根本不使用 NULL
”,将 NULL
重新定义为 nullptr
可能会破坏 bad/old 代码,而对帮助 good/new 代码。
C 并不简单地将 NULL
定义为 (void *)0
。也可以将其定义为 0
或任何计算结果为它的常量表达式。使用 #define NULL 0
在任何一种语言中都有效。
编辑:this answer非常好,也比较详细。
在
vcruntime.h
定义如下:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)