<p>int* arr = new int[rows]只是第一步,因为该语句仅分配了行指针数组,每行指针未初始化为有效地址,直接访问arri会解引用野指针而崩溃;必须循环执行arr[i] = new int[cols]为每行分配内存,并严格按先逐行delete[] arr[i]、再delete[] arr释放。</p>

new 分配二维数组指针时,为什么 int** arr = new int*[rows] 只是第一步?
因为 int** 本身只是指向指针的指针,它不等于一块连续的二维内存。你得先分配行指针数组,再为每一行单独分配列内存——否则访问 arr[i][j] 会崩溃。
常见错误是只做 new int*[rows] 就开始用 arr[i][j],此时每行指针都是野指针,解引用必崩。
- 必须逐行调用
new int[cols],且建议用循环确保每行都分配成功 - 若某次
new失败(如内存不足),前面已分配的行要手动delete[],否则内存泄漏 - 别用
new int[rows][cols]:这只能用于编译期确定列数的数组,返回的是int(*)[cols]类型,不是int**
用 std::vector<:vector>></:vector> 替代裸指针更安全吗?
是,但要注意性能和布局差异。它自动管理内存、支持边界检查(at())、可动态增行/增列,但每行内存不连续,缓存友好性差于一维模拟二维。
典型场景下它比裸指针更可靠,尤其当行长度不固定或需频繁插入删除时。
立即学习“C++免费学习笔记(深入)”;
- 初始化:
std::vector<:vector>> arr(rows, std::vector<int>(cols));</int></:vector> - 访问仍是
arr[i][j],语义一致 - 如果后续要传给 C 风格 API(比如需要
int**),不能直接转型,得额外拷贝或重构内存布局
如何用一维数组模拟二维并避免 int** 的麻烦?
用 int* arr = new int[rows * cols],然后通过 arr[i * cols + j] 访问。这是最紧凑、缓存友好的方式,也规避了多重 new 和释放风险。
缺点是语法上不支持 arr[i][j],必须自己算偏移;若行列数后期可能变化,需重分配并 memcpy。
- 释放只需一次
delete[] arr - 可封装成简单类或宏:
#define IDX(i,j) ((i)*(cols)+(j)),但注意宏无类型安全 - 与 C 接口兼容性好,很多图像/矩阵库(如 OpenCV)底层就用这种布局
释放 int** 时为什么不能只写 delete[] arr?
因为 arr 是指向指针数组的指针,它本身只占一行指针空间;真正分配的 rows 块列内存,每块都得单独 delete[]。
漏掉任何一行的 delete[],都会导致对应列内存泄漏;反过来说,对某行重复 delete[] 或错用 delete(而非 delete[])会引发未定义行为。
- 正确释放顺序:先循环
delete[] arr[i],再delete[] arr - 建议用 RAII 封装(如自定义类或
std::unique_ptr数组),避免手写释放逻辑 - 若使用
std::vector或一维模拟,释放逻辑天然简化,出错概率低得多
实际项目里,除非明确需要每行长度不同、或必须对接旧 C 接口,否则优先选 std::vector<std::vector<int>></int> 或一维模拟。裸 int** 容易在异常路径中漏释放,而手动管理释放顺序和匹配 new[]/delete[] 是最容易被忽略的细节。


















