第5.2章: 指针基础

English

概述

指针是存储地址的变量。它存的不是像 42 这样的值, 而是某个对象在内存中的位置。

在本章中,我们只介绍指针的语法:

我们只使用 ::std::size_t、int、 char 和 double 这类简单类型。容器将在后面第5.12章中讲解。

1. 包含 <memory> 以使用 ::std::addressof

要获取对象的地址,我们使用 ::std::addressof。 它定义在 <memory> 头文件中。


#include <memory>   // for ::std::addressof

我们使用 ::std::addressof 而不是 &,因为:

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));

打印指针有助于理解内存是如何布局的,以及指针之间如何相互关联。

要点