第5.4章: nullptr
概述
指针必须始终存储以下二者之一:
- 某个有效对象的地址,或者
- nullptr,表示“没有地址”。
在本章中,你将学到:
nullptr的含义- 为什么
nullptr比旧式的空指针常量更安全 - 如何用
nullptr初始化指针 - 为什么解引用
nullptr是未定义行为 - 为什么未定义行为并不保证会崩溃
- sanitizer 如何帮助检测空指针解引用
fast_io如何打印nullptr与nullptr_t
1. 什么是 nullptr?
nullptr 是一个特殊值,含义是:
“这个指针不指向任何对象。”
int *p = nullptr; // p points to nothing
这是表示空指针或未初始化指针的正确方式。
2. 为什么需要 nullptr
在 C++11 之前,程序员用 0 或 NULL 表示空指针。
这些做法有问题:
0是整数,不是指针NULL常常被定义为0- 重载决议可能选错函数
nullptr 修复了所有这些问题。它有自己的类型:
std::nullptr_t
并且它只会转换为指针类型。
3. 始终用 nullptr 初始化指针
未初始化的指针含有一个不可预测的地址。 使用它是未定义行为。
// Bad: uninitialized pointer
int *p;
// Good: always initialize
int *q = nullptr;
之后,当你有了真实的对象,就赋上它的地址:
#include
int value{42};
int *p = ::std::addressof(value);
4. nullptr 没有地址
空指针不指向任何内存位置。
p:
Address: 4000
Memory: [ null ] ← p stores “no address”
指针本身位于内存中的某处,但它的值是“没有地址”。
5. 打印 nullptr
在 fast_io 中,直接打印 nullptr_t 值会输出
字符串 "nullptr"。
#include <fast_io.h>
#include <cstddef>
std::nullptr_t np = nullptr;
println(np); // prints: nullptr
如果你想要空指针的数值表示,请使用
pointervw:
#include <fast_io.h>
using namespace ::fast_io::iomnp;
println(pointervw(nullptr));
这会打印指针宽度的完整数值地址,零扩展到平台的 指针大小。例如在 64 位系统上:
0x0000000000000000
这能确保所有指针(包括空指针)的格式化一致。
6. 不能解引用 nullptr
空指针不指向任何对象。 你不能解引用 nullptr。
int *p = nullptr;
// *p = 10; // undefined behavior — never do this
解引用 nullptr 永远是未定义行为。
7. 未定义行为并不保证会崩溃
许多初学者以为解引用 nullptr 一定会崩溃。
这是错的。
未定义行为的含义是:
- 程序可能崩溃
- 也可能破坏内存
- 也可能像什么都没发生一样继续运行
- 或者编译器可能基于错误的假设把代码优化掉
编译器可能假定指针永不为空
现代编译器会积极优化代码。 它们可能假定某个指针永不为空,从而移除检查或重排代码。
未启用内存标记的 WebAssembly 不会陷入陷阱
在 WebAssembly 上,如果没有启用内存标记,解引用 nullptr
通常不会触发陷阱。
虚拟机无法高效检测空指针解引用,因此程序可能带着被破坏的状态
继续运行。
这就是解引用 nullptr 极其危险的原因。
8. nullptr 在比较中
你可以检查一个指针是否为空:
int *p = nullptr;
if (p == nullptr)
{
// p does not point to anything
}
这常用于解引用之前:
if (p != nullptr)
{
int x = *p; // safe
}
9. nullptr 与指向指针的指针
指向指针的指针类型也可以为空。
int **pp = nullptr;
// **pp; // undefined behavior — pp does not point to a pointer
10. 使用 sanitizer 检测空指针解引用
现代编译器提供了在运行时检测空指针解引用的工具。
AddressSanitizer
-fsanitize=address
可检测:
- 空指针解引用
- 释放后使用
- 越界访问
内存标记(WebAssembly)
-fsanitize=memtag
可检测:
- 悬垂指针解引用
- 非法内存访问
这些工具让指针缺陷更容易被发现和修复。
要点
nullptr表示“这个指针不指向任何对象”。- 如果指针暂时没有目标,始终用
nullptr初始化它。 - 你不能解引用 nullptr。
- 解引用 nullptr 是未定义行为。
- 未定义行为并不保证会崩溃。
- 编译器可能假定指针永不为空。
- 未启用内存标记的 WebAssembly 在空指针解引用时不会触发陷阱。
fast_io把nullptr打印为字符串"nullptr"。pointervw(nullptr)打印指针宽度的完整数值地址。- 使用
-fsanitize=address或-fsanitize=memtag检测空指针解引用。