C++ 空指针(nullptr)

平时编写代码时,有时我们需要先定义一个指针变量,但当下还不确定要让它指向哪个变量。这时候应该往指针里存什么呢?

在 C++ 中,如果指针当前没有指向任何有效变量,就叫做 “空指针”。从 C++11 标准开始,C++ 提供了专门的 nullptr 关键字来表示空指针,其中 nullptr 就是英文 “null pointer(空指针)” 的缩写。

C++ 空指针的使用

我们在定义指针变量时,如果暂时还没有明确的目标变量,可以直接将它初始化为 nullptr。空指针的状态干干净净,一眼就能看出它当前没有记录任何有效地址。

如果把指针比作记录门牌号的便签纸,那么空指针就是一张 “空白便签纸”,上面干干净净,一眼就能看出还没有写地址。

C++ 空指针(nullptr)与普通指针的对比

语法:

类型名* 指针变量名 = nullptr;

说明:

无论是 int*、double* 还是 char*,任何类型的指针都可以使用 nullptr 来初始化,或者在不再需要指向某个变量时重新赋值为 nullptr。

这里小伙伴们一定要分清一点:“把指针设为 nullptr” 和 “定义指针时不初始化” 完全是两码事。未初始化的局部指针里面装的是随机的垃圾地址,非常危险。而赋值为 nullptr 的空指针状态明确可控,等后面有了目标变量,再把变量地址赋给它就可以了。

示例 1:定义空指针与重新置空

#include <iostream>

int main() {
    int score = 90;
    int* p = nullptr;    // 初始化为空指针

    p = &score;          // 指向变量 score
    std::cout << "*p = " << *p << '\n';

    p = nullptr;         // 重新置为空指针
    std::cout << "score = " << score << '\n';
    return 0;
}

运行结果如下。

*p = 90
score = 90

分析:

在这个例子中,int* p = nullptr; 表示指针 p 一开始处于空状态,没有指向任何变量。接着执行 p = &score; 让 p 保存变量 score 的地址,此时通过 *p 就能正常读取到 score 的值 90。

随后执行 p = nullptr; 让指针重新回到空状态。这里要注意,把 p 赋值为 nullptr 只是清空了 p 自己保存的地址记录,并不会影响原来的变量 score。因此,最后输出 score 的值依然是 90。

需要特别注意的是,我们不能直接对处于 nullptr 状态的空指针执行 *p 解引用。这在 C++ 中属于 “未定义行为”,也就是说运行结果不再可控,程序通常会直接崩溃退出。下面这种写法就是错误的。

int* p = nullptr;
std::cout << *p << '\n';    // 不能解引用空指针

判断指针是否为空

对空指针解引用会导致程序崩溃。因此在实际开发中,只要不确定指针当前有没有指向有效变量,我们在使用 *p 之前都应该先通过 if 语句检查它是否为空。

在条件判断中,p == nullptr 用来判断指针是否为空,而 p != nullptr 用来判断指针是否非空。只有确认指针不为空时,才能安全地执行 *p。

示例 2:判断指针非空后再访问

#include <iostream>

int main() {
    int age = 20;
    int* p = &age;

    if (p != nullptr) {
        std::cout << "*p = " << *p << '\n';
    }

    p = nullptr;
    if (p == nullptr) {
        std::cout << "p 是空指针" << '\n';
    }
    return 0;
}

运行结果如下。

*p = 20
p 是空指针

分析:

p != nullptr 表示检查指针 p 当前是否指向了有效目标。一开始 p 保存了变量 age 的地址,条件成立,程序进入第 1 个 if 分支并输出 *p 的值 20。

随后执行 p = nullptr 将指针置空,第 2 个条件 p == nullptr 成立,控制台输出对应的提示文字。

平时阅读别人的 C++ 代码时,我们还经常会看到直接把指针变量放进 if 条件里的简写形式。在布尔判断中,非空指针会自动转换为 true,而空指针 nullptr 会自动转换为 false。对于这个例子来说,下面 2 种方式是等价的。

// 方式 1(推荐)
if (p != nullptr) {
    std::cout << *p << '\n';
}

// 方式 2
if (p) {
    std::cout << *p << '\n';
}

对于初学的小伙伴来说,我们更推荐使用方式 1。把 != nullptr 明确写出来,一眼就能看出这是在检查指针是否为空,代码的可读性更好。同理,if (!p) 也等价于 if (p == nullptr),大家平时看别人代码时认得即可。

nullptr 与 NULL、0 的区别

如果你翻看过一些 C 语言或早期的 C++ 代码,可能会发现以前常使用 0 或 NULL 来表示空指针,比如写成 int* p1 = 0 或 int* p2 = NULL。

既然已经有 0 和 NULL 了,为什么 C++11 还要专门引入 nullptr 呢?这是因为在 C++ 中,0 本身是 int 类型的整数,而 NULL 也只是一个代表整数 0 的宏,它们都不是真正的指针类型。

在普通的指针初始化中这样写看不出问题,但一旦遇到函数重载,把整数当作空指针传参就会引发歧义或调错函数。下面我们通过一个例子来验证一下。

示例 3:空指针与函数重载

#include <iostream>

void show(int n) {
    std::cout << "调用整型版本:n = " << n << '\n';
}

void show(int* p) {
    if (p == nullptr) {
        std::cout << "调用指针版本(空指针)" << '\n';
    } else {
        std::cout << "调用指针版本:*p = " << *p << '\n';
    }
}

int main() {
    show(0);
    show(nullptr);
    return 0;
}

运行结果如下。

调用整型版本:n = 0
调用指针版本(空指针)

分析:

在这个例子中,我们定义了两个同名的 show() 重载函数,一个接收 int 整数,另一个接收 int* 指针。

调用 show(0) 时,因为 0 本身是 int 整数,编译器会直接匹配 show(int n) 整型版本。如果把实参换成 show(NULL),由于 NULL 在底层仍是整数类型,编译器要么误调整型版本,要么因无法判断匹配哪一个而直接报错。

而调用 show(nullptr) 时,因为 nullptr 是专门的空指针字面量,不会被当成普通整数,编译器就能准确匹配 show(int* p) 指针版本。

提示 平时编写 C++ 代码时,凡是需要表示空指针的地方,我们统一使用 nullptr 就可以了,不要再使用传统的 NULL 或 0。

上一篇:

下一篇:

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号