int* 是指向 int 的指针,即存储某个 int 变量地址的变量,该 int 再存储某个 int 的地址;需两次解引用才能访问最终 int 值,使用前必须确保两级指针均有效且已初始化。

什么是 int**,它到底指向谁?
不是“指针的指针”这个说法有多玄,而是它描述了一个两级间接寻址关系:int** p 是一个变量,它存的是某个 int* 类型变量的地址,而那个 int* 又存着某个 int 的地址。中间多了一层跳转,所以必须分两步解引用才能拿到最终值。
常见错误是以为 **p = 5 就能直接初始化——其实此时 p 本身没指向有效的 int*,更别说那层 int* 是否指向有效 int 了。空悬或未初始化的 int** 解引用会崩溃。
- 声明后必须先让
p指向一个合法的int*变量(比如取地址) - 那个被指向的
int*也得已初始化,指向有效内存(栈/堆) -
int** p = nullptr;是安全的;但**p = 5;是未定义行为
怎么安全地分配和使用 int** 指向堆上二维数组?
典型场景:动态申请 m x n 整数矩阵。用 int** 实现“指针数组 + 每行独立分配”,比连续一块 malloc 更灵活(比如每行长度不同),但也更易出错。
关键点在于两层分配、两层释放,且顺序不能反:
int** matrix = new int*[m]; // 分配 m 个 int* 的数组 for (int i = 0; i
- 别漏掉任何一层
delete[],否则内存泄漏 -
matrix[i]是int*,所以用delete[];matrix是int**,本质是int*[],同样用delete[] - 如果用
malloc,对应要free,但类型转换容易出错,C++ 推荐用new/delete
void func(int** p) 传参时为什么常要传 &ptr?
函数内部想修改调用方的指针变量本身(比如让它指向新分配的内存),就必须传指针的地址。因为 C++ 默认按值传递,func(ptr) 传进去的是 ptr 的副本,改它不影响原变量。
例如:函数内部分配内存并让外部指针生效:
void create_2d(int**& p, int m, int n) { // 引用传参,更简洁
p = new int*[m];
for (int i = 0; i
- 若不用引用,就得写
void func(int*** p),调用时传&arr,三层星号极易混淆 - 传
int**值参数只能修改它指向的内容(比如**p = 10),改不了p本身存的地址 - 现代 C++ 更倾向用
std::vector<:vector>></:vector>避免手动管理
什么时候该避免用 int**?
绝大多数日常场景下,int** 是过度设计。它带来的灵活性常被内存碎片、释放复杂度和空指针风险抵消。
- 固定大小二维数组:直接用
int arr[10][20]或std::array<:array>, 10></:array> - 动态但规则矩阵:用一维
std::vector<int></int>+ 行列计算(vec[i * cols + j]),缓存友好 - 需要不同行长的“锯齿数组”:用
std::vector<:vector>></:vector>,自动管理内存,支持移动语义 - 跨函数共享数据:优先考虑智能指针如
std::unique_ptr<int></int>或std::shared_ptr<:vector>></:vector>
裸 int** 真正不可替代的场合极少,比如对接 C 风格 API(如 char** argv)或极低层内存池实现——其他时候,它只是给调试增加一层间接跳转而已。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











