在Clang 6.0 的stddef.h文件中可以找到NULL和nullptr的声明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#undef NULL
#ifdef __cplusplus
# if !defined(__MINGW32__) && !defined(_MSC_VER)
# define NULL __null
# else
# define NULL 0
# endif
#else
# define NULL ((void*)0)
#endif
#ifdef __cplusplus
#if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED)
namespace std { typedef decltype(nullptr) nullptr_t; }
using ::std::nullptr_t;
#endif
#endif
|
早在1972年,C语言诞生的初期,常数0带有常数及空指针的双重身分。 C使用preprocessor macro NULL表示空指针,让NULL及0分别代表空指针及常数0。 NULL可被定义为((void*)0)或是0。
C++并不采用C的规则,不允许将void*隐式转换为其他类型的指针。为了使代码char* c = NULL;能通过编译,NULL只能定义为0。这样的决定使得函数重载无法区分代码的语义:
1
2
|
void foo(char *);
void foo(int);
|
C++建议NULL应当定义为0,所以foo(NULL);将会调用foo(int),这并不是程序员想要的行为,也违反了代码的直观性。0的歧义在此处造成困扰。
C++11引入了新的关键字来代表空指针常数:nullptr,将空指针和整数0的概念拆开。 nullptr的类型为nullptr_t,能隐式转换为任何指针或是成员指针的类型,也能和它们进行相等或不等的比较。而nullptr不能隐式转换为整数,也不能和整数做比较。
为了向下兼容,0仍可代表空指针常数。
1
2
3
4
5
|
char* pc = nullptr;
// OK
int * pi = nullptr;
// OK
int i = nullptr;
// error
foo(pc);
// 呼叫foo(char *)
|
PS:__MINGW32__是MinGW编译器的预定义宏。_MSC_VER是微软C/C++编译器——cl.exe 编译代码时预定义的一个宏。_MSC_VER的值表示cl的版本。需要针对cl特定版本编写代码时,也可以使用该宏进行条件编译。