前言
C和C++的变量名是对大小写敏感的,因此NULL和null并不是一回事,前者是C/C++中的系统关键字,null并不是。C++11以后又引入了nullptr,用以解决NULL在隐式转换和作为函数传入参数时的二义性问题。
在C++11以前,在C/C++语言中,我们常常用NULL作为指针变量的初始值。而在C++11之后,却不建议你这么做。
其实NULL根据命名全大写可以看出来,它是一个常量,既然是常量,就需要进行宏定义。C语言的标准头文件是这样定义的
#define NULL ((void*)0)
而到了C++中,则变成了
#define NULL 0
查阅stddef.h,可以看到如下定义
#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
从定义中可以看出,C++中,NULL其实就是0,但是也可以用作空指针,只是用作空指针可能是为了兼容C,迫于无奈。
以下一段代码可以很好地解释NULL存在的问题:
#include<iostream>
using namespace std;
void test(void *p)
{
cout<<"p is pointer "<<p<<endl;
}
void test(int num)
{
cout<<"num is int "<<num<<endl;
}
int main(void)
{
test(NULL);
return 0;
}
这时,如果编译的话,会报以下错误,
$ g++ -o test test.cpp
main.cpp: In function ‘int main()’:
main.cpp:14:14: error: call of overloaded ‘test(NULL)’ is ambiguous
test(NULL);
很明显,NULL存在二义性,它既是整数,也是一个指针,函数test()无法根据参数的数据类型判断应该调用哪一个实现。
这时使用nullptr的优越性就体现出来了,因为它可以很好地把空指针这一层意思给剥离出来。nullptr就是C++11为了解决这个痛点而推出的东西。
test(nullptr);
就会自然而然地走到指针的那个函数里。因此,以后若想使用整数特性,就赋值为0,若想使用指针特性,就赋值为nullptr,这样一目了然,减少了未知的Bug的可能性。
多说一句,为什么要作此改动,我想首先应该是C和C++在处理void 类型的时候存在一定的区别。C语言中,void 类型的变量可以赋值给任意类型的指针,也可以被任意类型的指针赋值,两个方向都不会报错。但是C++具有更严格的类型检查,前者是不被允许的。
因此下面一段C语言代码是可以编译通过的
int main()
{
void* a;
int* b=a;
}
但是下面的C++代码就会报错
test.cpp:4:7: error: cannot initialize a variable of type 'int *' with an lvalue of type 'void *'
int* b=a;
与此同时,在malloc上,也存在类似的问题:
int len = 100;
int p = malloc(len * sizeof(int)); // C推荐做法
int p = (int )malloc(len * sizeof(int)); // C++推荐做法
malloc函数返回值得类型是 void*,C不要求强制类型转换,会自动进行隐式转换,但是C++则需要,因为void* 不能转换成其他类型的指针。