空指针NULL 和 What exactly is nullptr?

本文探讨了C++中表示空指针的方法,对比了NULL和0的使用场景及局限性,并阐述了nullptr的引入及其在解决指针一致性问题上的优势。

原文链接:http://www.cnblogs.com/carter2000/archive/2012/06/26/2564369.html


Stack Overflow 中讨论 http://stackoverflow.com/questions/1282295/what-exactly-is-nullptr



C++里怎么样表示空指针

  在C++11(C++0x)里,空指针有了一个官方版的表示:nullptr。在此之前C++里的空指针应该怎么表示呢?

  一种方法是定义一个NULL宏(#define NULL 0)来表示空指针,虽然本质上和直接写成0一样,但NULL能相对直观地表示这是个指针。

  一些知名头文件里就带有NULL的定义,比如windows.h和stdio.h。直接用NULL似乎是个不错的主意。

  有时候源文件中并不需要引用到这些知名头文件,那么NULL就要由自己来定义。这样,可能在很多地方都充斥着NULL的定义,看着就觉得不是很痛快。另外,NULL可以由自己定义,这意味着NULL完全有可能被定义成其他东西(少见)。因为NULL并非标准,所以有人可能自己定义了Null或null或其他东西(少见),这会带来混乱。

  那么直接用0来表示空指针好了。用0表示写起来简单,而且0就是0,不会有其他定义,只是不那么直观。

  但是用0也好,用NULL也好,都有一个先天劣势,就是在类型匹配时它会被优先当成整数,在一些情况下会带来麻烦。比如:

复制代码
void fun(int*)
{
    std::cout << "pointer" << std::endl;
}

void fun(int)
{
    std::cout << "int" << std::endl;
}
复制代码

  上面的fun有两种重载形式,一种是以整数为参数,一种是以整数指针为参数。假设现在,想要传空指针给fun,要怎么写。

  写成fun(0),毫无疑问,这里打印的会是“int”,因为0被当成一个整数。为了让它调用第一个版本的函数,我们需要明确的传递一个指针,写成fun((int*)0),函数调用正确。

  假设还有一个fun(char*)版本的函数,那岂不是要写成fun((char*)0)。吹毛求疵的人可能会觉得这样不好,因为空指针的表示不一致,需要根据不同情况做强制转化。

  这时,NULL又被想起,我们可以这样定义#define NULL ((void*)0),这样NULL就真真切切是个指针了。在C语言里,这是行得通的。可惜这在C++里不行,因为C++中void*并不能隐式转换成int*或其他类型的指针。仍旧需要fun((int*)NULL)和fun((char*)NULL),这样更丑。

  既然强制转换无可避免,相比之下,还是0好一点。

  但实际上前面提到的两种方法不只不恰当,严格来说还不准确。早期C++著作The Annotated C++ Reference Manual(ARM)里指出:“Note that the null pointer need not be represented by the same bit pattern as the integer 0.”也就是说空指针并不一定是0。因此不管是直接写成0,还是用宏来表示都不准确的。只是大多数人(包括我)一直这么用,也一直没出什么问题。

  工作时因为项目早期代码基本都是采用NULL,为了保持编码风格的一致性,我也一直沿用这一写法。

  而平时偶然写代码则更倾向于直接使用0,最直接的原因是每次要使用NULL时都要关注一下它是不是已经不定义了,在没定义的情况下自己还要定义一个,感觉挺麻烦的。

  总的结论是:具体的选择只是看个人喜好。


是的,`nullptr` 是 C++11 引入的**空指针常量**,用于明确表示指针不指向任何对象或函数,它与 C 语言中的 `NULL` 有本质区别,具体如下: --- ### **1. `nullptr` 的特性** - **类型安全**:`nullptr` 是 `std::nullptr_t` 类型的常量,能隐式转换为任何指针类型(如 `int*`、`char*` 等),但**不会**被隐式转换为整数类型(如 `int`)。 - **避免重载歧义**:在函数重载中,`nullptr` 能明确区分指针参数整数参数,而 `NULL` 可能因隐式转换导致歧义。 - **示例**: ```cpp void foo(int* ptr) { /* ... */ } void foo(int num) { /* ... */ } int main() { foo(nullptr); // 调用 foo(int*) foo(NULL); // 可能调用 foo(int)(若 NULL 定义为 0) } ``` --- ### **2. C 语言中的 `NULL`** - **定义**:`NULL` 是 C 标准中定义的宏,通常表示为 `(void*)0` 或简单的 `0`(由实现决定)。 - **类型不安全**:`NULL` 在 C 中可能被隐式转换为整数类型,导致潜在错误。 - **示例**: ```c #include <stdio.h> void foo(int* ptr) { printf("Pointer\n"); } void foo(int num) { printf("Integer\n"); } int main() { foo(NULL); // 可能调用 foo(int)(若 NULL 定义为 0) return 0; } ``` --- ### **3. 关键区别** | 特性 | `nullptr` (C++11) | `NULL` (C/C++) | |---------------------|---------------------------------|-----------------------------| | **类型** | `std::nullptr_t` | 通常为 `(void*)0` 或 `0` | | **隐式转换** | 仅指针类型 | 指针或整数类型 | | **重载解析** | 明确匹配指针参数 | 可能匹配整数参数 | | **线程安全** | 是(常量) | 是(宏展开为常量) | | **可读性** | 明确表示空指针 | 依赖实现,可能混淆 | --- ### **4. 代码对比** #### **C++ 中推荐使用 `nullptr`** ```cpp #include <iostream> void process(int* ptr) { std::cout << "Pointer\n"; } void process(int num) { std::cout << "Integer\n"; } int main() { process(nullptr); // 输出 "Pointer" process(NULL); // 可能输出 "Integer"(若 NULL=0) return 0; } ``` #### **C 中必须使用 `NULL`** ```c #include <stdio.h> int main() { int* ptr = NULL; // 合法,但类型不明确 if (ptr == 0) { // 显式比较 printf("ptr is NULL\n"); } return 0; } ``` --- ### **5. 最佳实践** - **C++**:始终使用 `nullptr` 替代 `NULL` 或 `0`,以提高代码安全性可读性。 - **C**:使用 `NULL`(或 `0`),但需注意类型隐式转换问题。 - **C++11 之后**:`NULL` 仍可用,但已逐渐被弃用。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值