在C++11之前,一般使用NULL代表空指针。
NULL的定义在C和C++中不同,而且C和C++针对0和指针之间的运算规则也存在差异:
- C++03标准:空指针常量是整数类型的整型常量表达式右值,其值为零。空指针常量可以转换为指针类型;结果是该类型的空指针值。
- C99标准:空指针常量是值为 0 的整型常量表达式,或者转换为 void * 类型的此类表达式。
这两个定义导致了冲突,C++中的NULL如果和C语言一样也是(void *)0指针,而C++不允许void*隐式转换成其它指针类型,那NULL就无法表示空指针了。于是,对于大部分头文件,NULL的定义需要根据C和C++区分定义:
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif
- 在 C++03 标准中,空指针常量是一个整型常量表达式,其值为零,这确保了它可以被转换为任意指针类型,产生空指针值。
- 在 C99 标准中,空指针常量一般是整型常量 0,或者是
(void*)0
。这种定义是为了确保了空指针与任意对象或函数指针的比较结果都不相等。
但是,无论是C99还是C++03的定义,都存在缺陷:
- 如果NULL是((void *)0)指针:
- 类型转换问题:无法在C++中隐式转换为其他任意类型的指针,无法在C++体系下实现空指针能力。和C++体系不相容。
- 误用问题:在参与指针运算时不会报错,存在可能的误用。
- 如果NULL是整数0:
- 类型安全问题:因为 NULL 实际上是整数 0,所以它可以隐式转换为任何指针类型,但也可以转换为任何整数类型,这可能导致类型安全问题。
- 模板推导问题:在模板代码中,如果使用 NULL 作为参数,编译器会将其视为整数,而非指针类型。
- 重载问题:如果一个函数重载了接受整数和指针两种类型的参数,使用 NULL 可能会调用错误的函数重载。
- 误用问题:在参与算数运算时不会报错,存在可能的误用。
C++11 引入 nullptr 彻底解决了这些问题,提高语言的安全性、清晰性和现代性。
在C++11的定义里,nullptr是一个特殊类型的字面量,类型是 std::nullptr_t
,可以转换为任何指针类型,但不可以转换为整数类型,不能参与整数算术运算。因此,使用 nullptr可以增加代码的清晰性,避免类型安全问题。即使在混合C++和C代码时,nullptr也提供了一个明确无误的空指针表示,达到兼容的目的。并且nullptr不能参与算术运算,避免了可能的误用。
现在很多头文件定义了NULL,使其依然可用。曾经看到一些同学把NULL当作0或'\0'使用,其实也是有问题的。
在了解NULL和nullptr背后的故事之后,相信大家以后再也不会用NULL了。