用 new intn 分配的二维数组只能用 delete[] 释放,但因类型不匹配属未定义行为;正确做法是用一维分配加索引映射或 std::vector;若用 new int*[n] 配合循环 new int[m],则须先逐行 delete[] p[i] 再 delete[] p。

new int[n][m] 分配的二维数组不能直接 delete[]
用 new int[n][m] 分配的“伪二维数组”本质是连续一块内存,但类型是 int (*)[m](指向含 m 个 int 的数组的指针),不是 int**。直接写 delete[] p 语法合法,但行为未定义——因为编译器不保证按二维布局析构,且多数情况下不会出错只是侥幸,但严格标准下属于 UB。
实操建议:
- 优先改用
std::vector<:vector>></:vector>或一维std::vector<int></int>手动映射(如vec[i * m + j]),彻底避开裸 new/delete - 若必须用裸指针,应分配一维内存并手动计算索引:
int* p = new int[n * m];,释放时只写delete[] p; - 不要尝试
delete[][] p—— C++ 语法根本不支持这个写法
new int*[n] 配合循环 new int[m] 必须配对释放
这是模拟“真正指针数组”的常见写法,但释放时容易漏掉内层循环或顺序颠倒。错误示例:delete[] p; 只释放了指针数组本身,int* 指向的每行内存都泄漏了。
正确释放必须两步:
- 先逐行
delete[] p[i];(注意是delete[],因为每行是new int[m]) - 再
delete[] p;释放指针数组 - 释放前检查
p != nullptr,且每行p[i] != nullptr(尤其在部分分配失败后)
典型代码片段:
int** p = new int*[n]; for (int i = 0; i <h3>delete 和 delete[] 混用会触发未定义行为</h3><p>这是最隐蔽也最危险的坑。比如用 <code>new[]</code> 分配却用 <code>delete</code>(无方括号)释放,或反过来。编译器通常不报错,但可能:</p>
- 只调用第一个元素的析构函数(对类类型)
- 释放错误的内存块大小,导致堆损坏
- 在某些平台(如 Windows CRT 调试模式)直接断言失败,报错
_CRT_BLOCK is not valid或类似信息
牢记口诀:new 对应 delete,new[] 对应 delete[];类型无关,只看分配方式。
用 RAII 替代手写 delete 是更安全的选择
手动管理二维内存极易出错,尤其涉及异常路径时。现代 C++ 中,几乎没有任何理由坚持裸指针二维数组。
替代方案推荐:
-
std::vector<:vector>></:vector>:语义清晰,自动管理所有内存,支持动态行列 -
std::vector<int></int>+ 封装访问函数:auto at(int i, int j) { return data[i * cols + j]; },零额外开销,缓存友好 - C++23 引入的
std::mdspan(需编译器支持):提供多维视图,不拥有内存,配合 vector 使用很灵活
复杂点在于:二维裸数组的释放逻辑看似简单,但边界条件(分配失败、部分释放、异常中断)会让手写代码迅速变得脆弱。别低估这几行 delete[] 背后的责任。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











