第5.2章: 指针基础
概述
指针是存储地址的变量。它存的不是像 42 这样的值,
而是某个对象在内存中的位置。
在本章中,我们只介绍指针的语法:
- 如何声明指针(包括指向指针的指针)
- 如何使用
::std::addressof获取地址 - 如何把该地址存入指针
- 为什么指针必须始终初始化
- 如何用
pointervw打印指针 - 如何用简单的伪图来可视化指针
我们只使用 ::std::size_t、int、
char 和 double 这类简单类型。容器将在后面第5.12章中讲解。
1. 包含 <memory> 以使用 ::std::addressof
要获取对象的地址,我们使用 ::std::addressof。
它定义在 <memory> 头文件中。
#include <memory> // for ::std::addressof
我们使用 ::std::addressof 而不是 &,因为:
- 它总是返回对象的真实地址
- 即使某个类型重载了
operator&,它也能正确工作 - 它是推荐的现代 C++ 做法
2. 什么是指针?
内存中的每个对象都有一个地址。指针就是存储这种地址的变量。
Address: 2000 2001 2002 2003
Memory: [ 7 ][ 7 ][ 7 ][ 7 ]
↑
x lives here
#include <memory>
::std::size_t x{7};
::std::size_t *p = ::std::addressof(x); // p stores the address of x
3. 声明指针
指针类型写作 T*,表示“指向 T 的指针”。
在本教程中,我们更倾向于把 * 写在变量名旁边:
int *ip;
char *cp;
::std::size_t *sp;
double *dp;
始终初始化指针
你必须始终初始化指针。 未初始化的指针含有一个不可预测的地址,使用它是未定义行为。
int *p = nullptr; // safe: p points to nothing
char *q = nullptr; // safe
::std::size_t *r = nullptr; // safe
之后,当你有了真实的对象,就可以把它的地址赋给它:
int value{42};
int *p = ::std::addressof(value);
一个常见的初学者陷阱
这个声明具有误导性:
int* p, q; // q is NOT a pointer
只有 p 是指针。q 是一个普通的 int。
把 * 写在变量名旁边能让含义更清楚:
int *p, q; // p is a pointer, q is an int
更好的做法是每行只声明一个指针,并且始终初始化它:
int *p = nullptr;
int *q = nullptr;
4. 指向指针的指针
指针也可以存储另一个指针的地址。 这被称为指向指针的指针。
int value{42};
int *p = ::std::addressof(value); // p points to value
int **pp = ::std::addressof(p); // pp points to p
value:
Address: 3000
Memory: [ 42 ]
p:
Address: 4000
Memory: [ 3000 ] ← p stores address of value
pp:
Address: 5000
Memory: [ 4000 ] ← pp stores address of p
你可以继续增加 * 的层数,但指向指针的指针是最常见的形式。
5. nullptr
指针也可以存储“没有地址”。
这写作 nullptr。
int *p = nullptr; // p does not point to anything
int **pp = nullptr; // pp also does not point to anything
我们会在第5.4章中更深入地探讨 nullptr。
6. 打印指针
指针存储一个地址,有时你想打印这个地址,以查看某个东西在内存中的位置。
在 fast_io 中,打印指针的推荐方式是使用
pointervw 操纵器。
要使用它,请引入命名空间:
using namespace ::fast_io::iomnp;
然后你就可以这样打印一个指针:
#include <memory>
#include <fast_io.h>
using namespace ::fast_io::iomnp;
int value{42};
int *p = ::std::addressof(value);
println(pointervw(p));
这会以可读且一致的格式打印 p 中存储的地址。
具体数字取决于你的系统,但概念上它看起来像:
0x0000000000007d30
你可以用这种方式打印任何指针,包括指向指针的指针:
int **pp = ::std::addressof(p);
println(pointervw(pp));
打印指针有助于理解内存是如何布局的,以及指针之间如何相互关联。
要点
- 指针存储地址,而不是值。
T*表示“指向 T 的指针”。- 写
int *p;而不是int* p;,以避免混淆。 - 用
::std::addressof(来自 <memory>)获取地址。 - 始终初始化指针(需要时用
nullptr)。 - 指针本身也是变量,也有自己的地址。
- 你可以使用
int **pp这样的指向指针的指针类型。 - 用
::fast_io::iomnp中的pointervw打印指针值。 - 本章只关注语法;容器将在后面的第5.12章中讲解。