第5.3章: 解引用
概述
指针存储一个地址。解引用意味着: 用该指针访问位于那个地址上的对象。
在本章中,你将学到:
- 如何用
*p解引用指针 - 如何通过指针读取和写入
- 如何解引用指向指针的指针
- 为什么解引用无效指针是严重的内存安全漏洞
- 如何用 sanitizer 检测无效解引用
- 如何用伪图可视化解引用
1. 解引用是什么意思?
如果指针存储了一个地址,那么解引用就是“走到那个地址, 访问存储在那里的对象”。
value:
Address: 3000
Memory: [ 42 ]
p:
Address: 4000
Memory: [ 3000 ] ← p stores address of value
#include <memory>
int value{42};
int *p = ::std::addressof(value);
int x = *p; // x becomes 42
2. 通过指针读取
解引用让你能读取指针地址处存储的值。
int n{100};
int *p = ::std::addressof(n);
int copy = *p; // copy becomes 100
3. 通过指针写入
解引用同样让你能修改指针所指的对象。
int n{5};
int *p = ::std::addressof(n);
*p = 20; // changes n to 20
4. 解引用指向指针的指针
如果你有指向指针的指针,可以解引用两次。
int value{7};
int *p = ::std::addressof(value);
int **pp = ::std::addressof(p);
int x = **pp; // x becomes 7
value:
Address: 3000
Memory: [ 7 ]
p:
Address: 4000
Memory: [ 3000 ]
pp:
Address: 5000
Memory: [ 4000 ]
**pp → *p → value → 7
5. 解引用 nullptr 是未定义行为
在解引用之前,指针必须指向一个有效对象。
解引用 nullptr 是未定义行为。
int *p = nullptr;
// *p = 10; // undefined behavior — never do this
6. 解引用无效指针是严重的内存安全漏洞
解引用无效指针不只是 bug —— 它是一个 严重的内存安全漏洞。它可能导致:
- 内存破坏
- 数据泄漏
- 崩溃
- 安全漏洞利用
一个指针在以下情况是无效的:
- 空(
nullptr) - 未初始化
- 悬垂(对象已不存在)
- 伪造(手工赋了一个任意数值)
- 越界
示例
// 1. Null pointer
int *p = nullptr;
// *p = 10; // undefined behavior
// 2. Uninitialized pointer
int *q;
// *q = 10; // undefined behavior
// 3. Dangling pointer
int *r = nullptr;
{
int x{5};
r = ::std::addressof(x);
} // x is destroyed here
// *r = 10; // undefined behavior
// 4. Forged pointer
int *s = reinterpret_cast(12345);
// *s = 10; // undefined behavior
规则: 只有当你绝对确定某个指针指向一个有效、存活的对象时,才解引用它。
7. 使用 sanitizer 检测无效解引用
现代编译器提供了在运行时检测无效指针解引用的工具。 开发期间你应当始终启用它们。
AddressSanitizer
-fsanitize=address
可检测:
- 空指针解引用
- 释放后使用
- 越界访问
- 栈与堆缓冲区溢出
内存标记(WebAssembly)
-fsanitize=memtag
可检测:
- 悬垂指针解引用
- 错误的指针算术
- 非法内存访问
这些工具让指针缺陷更容易被发现和修复。
8. 打印解引用得到的值
解引用得到的值可以像其他任何值一样打印。
#include <fast_io.h>
#include <memory>
int n{55};
int *p = ::std::addressof(n);
println(*p); // prints 55
要点
*p表示“p所指向的对象”。- 你可以通过指针读取和写入。
- 指向指针的指针类型可以多次解引用。
- 解引用 nullptr 是未定义行为。
- 解引用未初始化、悬垂、伪造或越界的指针都是未定义行为。
- 无效解引用是严重的内存安全漏洞。
- 使用
-fsanitize=address或-fsanitize=memtag检测无效解引用。