指针的指针
“指针的指针”即一个指针,其保存的内容是“另一个指针的地址”。
- 指向
int
的一级指针使用一个星号:int* ptr; // 指向 int 的指针,一个星号
- 指向“指向
int
的指针”的指针使用两个星号:int** ptrptr; // 指向 int* 的指针,两个星号
其使用方式与普通指针一致:先解引用获得一级指针值,再解引用一级指针取得最终数据;可连续解引用:
```cpp
int value{ 5 };
int* ptr{ &value };
std::cout << *ptr << '\n'; // 第一次解引用,输出 5
int** ptrptr{ &ptr };
std::cout << **ptrptr << '\n'; // 两次解引用,仍输出 5
注意:不能直接把“值”的地址再取地址:
```cpp
int value{ 5 };
int** ptrptr{ &&value }; // ❌ 非法,&value 为右值,operator& 需左值
但可将指针的指针初始化为空指针:
```cpp
int** ptrptr{ nullptr };
指针数组
指针的指针常用于动态分配“指针数组”:
```cpp
int** array{ new int*[10] }; // 分配含 10 个 int* 的动态数组
与常规动态数组的唯一区别是元素类型为 int*
而非 int
。
二维动态数组
指针的指针另一常见用途是实现动态二维数组(回顾《C 风格多维数组》)。
固定二维数组可简单声明:
```cpp
int array[10][5];
动态分配二维数组则稍显复杂。若最右维度为 constexpr
,可写:
```cpp
int x{ 7 }; // 运行期值
int (*array)[5]{ new int[x][5] }; // 括号必不可少
括号确保 array
为“指向含 5 个 int
的数组的指针”。若无括号,编译器会将其解释为“含 5 个 int*
的数组”。
可用 auto
简化:
```cpp
int x{ 7 };
auto array{ new int[x][5] }; // 简洁得多
但若最右维度非常量,则必须分步构造:
- 先分配指针数组(行);
- 再为每行独立分配列数组。
```cpp
int** array{ new int*[10] }; // 10 行
for (int count{ 0 }; count < 10; ++count)
array[count] = new int[5]; // 每行 5 列
访问方式与静态二维数组相同:
```cpp
array[9][4] = 3; // 等价于 (array[9])[4] = 3;
利用此方法,各行列数可不同,例如生成三角矩阵:
```cpp
int** array{ new int*[10] };
for (int count{ 0 }; count < 10; ++count)
array[count] = new int[count + 1]; // 第 i 行有 i+1 列
释放须按相反顺序:先逐行 delete[]
,再 delete[]
行指针数组:
```cpp
for (int count{ 0 }; count < 10; ++count)
delete[] array[count];
delete[] array; // 最后释放行指针数组
若先释放 array
再释放各行,会访问已回收内存,导致未定义行为。
扁平化二维数组
由于二维动态数组分配/释放复杂且易出错,常将二维数组(x×y)扁平成一维数组(x*y):
```cpp
// 不再写:
int** array{ new int*[10] };
for (int count{ 0 }; count < 10; ++count)
array[count] = new int[5];
// 而是写:
int* array{ new int[50] }; // 10×5 扁平成一维
用简单公式把二维坐标映射到一维索引:
```cpp
int getSingleIndex(int row, int col, int numberOfColumnsInArray)
{
return row * numberOfColumnsInArray + col;
}
// 将扁平数组的 [9][4] 设为 3
array[getSingleIndex(9, 4, 5)] = 3;
按地址传递指针
与通过指针形参修改实参值类似,可把“指针的指针”传给函数,以改变指针本身指向。不过,若函数需修改指针实参指向何处,更推荐使用“指针的引用”(参见《按地址传递(下)》)。
多级指针
可继续声明三级指针:
```cpp
int*** ptrx3;
用于动态三维数组,但需嵌套循环,极易出错。
甚至可声明四级指针:
```cpp
int**** ptrx4;
或更多层,但实践中极少需要如此深度的间接寻址。
结论
除非别无选择,否则应避免使用指针的指针:
- 使用复杂;
- 极易因双重解引用而误碰空指针或悬空指针。