C/C++中的NULL、0和nullptr的区别
在 C/C++
中,为了防止野指针(指针指向的位置是不可知的),我们常常需要将指针赋值为空指针。在
C
中我们用 NULL
来表示空指针,而在
C++
中则有 NULL
和 nullptr
两种空指针,并且其 NULL
的定义也和 C
语言中的完全不同。我们就在这篇文章中仔细研究一下。
NULL
在 C
语言中,我们使用 NULL
来表示空指针。他是一个宏定义,其定义是 1
#define NULL ((void *)0)
NULL
其实是一个 void*
类型的指针。并且在
C
语言中,任何类型的指针都可以(隐式地)转换为
void *
类型,反过来也成立。但是在 C++
中则不存在这种隐式的转换。比如在 C
语言中, 1
int *i = (void *)0;
C++
编译器则会报错。在
C++
中 NULL
被定义为整数 0
:
1
2
3
4
5
6
7#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
0
因此在 C++
中,定义一个空指针: 1
int *p = NULL;
p
赋值为零。在 C++
中为0的指针都会被认为是空指针。 但是这样的话,就会造成一些歧义:0
即可以表示整数常量,又可以表示空指针常量。那么在下面的经典例子中:
1
2
3
4
5void foo(int) {} // #1
void foo(char*) {} // #2
int main() {
foo(NULL);
}foo(NULL)
到底应该调用哪个函数?既然
NULL
是指针,它就应该调用 #2
,但实际上
NULL
又被定义为整数0,所以会调用 #1
.
为了解决这种歧义性,于是又引入了一种新的空指针常量
nullptr
.
nullptr
在 C++11
中引入了一个新的关键字
nullptr
,其定义为 1
typedef decltype(nullptr) nullptr_t;
decltype
也是 C++11
中新引入的关键字,用于自动类型推导。
nullptr_t
是一种数据类型,而nullptr
是一个常数,是该类型的一个实例。- 因此可以通过
nullptr_t
类型创建另一个新的实例。但所有定义为nullptr_t
类型的数据都是等价的,行为也是完全一致的。 nullptr
是一个编译期常量,其类型为nullptr_t
,它既不是整型类型,也不是指针类型。
除此之外,nullptr
还有一些其他的特性:
std::nullptr_t
类型并不是指针类型,但可以隐式转换成任意一个指针类型。nullptr_t
类型数据不能转换为非指针类型,即使使用reinterpret_cast<nullptr_t>()
的方法也不行。nullptr_t
类型数据不适用于算术运算表达式。nullptr_t
类型数据可以用于关系运算表达式,但仅能与nullptr_t
类型数据或者指针类型数据进行比较,当且仅当关系运算符为==,<=,>=
等时返回true。
这些特性可以在代码中得到验证: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//nullptr可以隐式转换为char*
char *cp = nullptr;
//nullptr不能转换为非指针类型,任何类型也不能转换为nullptr_t
int i1 = nullptr; //error
int i2 = reinterpret_cast<nullptr_t>(nullptr); //error
//nullptr与nullptr_t类型变量可以作比较,当使用==,<=,>=符号作比较时才返回true
nullptr_t nptr;
if (nptr == nullptr) //nullptr_t nptr == nullptr
if (nptr < nullptr) //nullptr_t nptr >= nullptr
//不能转换为整型或者bool类型
if (0 == nullptr); //error
if (nullptr); //error
//不能进行算术运算
nullptr += 1; //error
nullptr * 5; //error
与 void*
区别
- 在
C++11
标准中,nullptr
类型数据所占用的内存空间,与void*
所占内存空间大小相同,即sizeof(nullptr_t) == sizeof(void*)
. - 语法内涵不同,
nullptr
是一个编译期常量,而(void*)0
只是一个强制转换表达式,其返回的也是一个void*
指针类型。 - 在
C++
语言中,nullptr
到任何指针的转换是隐式的,而(void*)0
则必须经过类型转换后才能使用。
nullptr
可能的实现
1 |
|
源码这部分需要继续学习~