int arr3是连续内存块而非嵌套指针,按行优先布局,访问arri等价于((int)arr + i4 + j);类型为int()[4],非int**;传参需指定列数,sizeof在函数内退化为指针大小。

用 int arr[3][4] 定义二维数组,不是“嵌套指针”
很多人以为 int arr[3][4] 是“指向指针的指针”,其实它是连续一块内存,总长 12 个 int。编译器靠行优先(row-major)布局算下标:访问 arr[i][j] 实际等价于 *((int*)arr + i * 4 + j)。
常见错误现象:int** ptr = arr; 编译失败 —— 类型不匹配,int (*)[4] 和 int** 完全不同。
- 定义时必须指定除最外层外的所有维度大小,
int arr[][4]合法(可由初始化推导行数),但int arr[][]非法 - 传参给函数时,形参必须写成
void f(int arr[][4], int rows)或void f(int (*arr)[4], int rows),不能只写int** arr - 用
sizeof(arr)能拿到整个数组字节数(如 48),但传进函数后退化为指针,sizeof就只剩指针大小了
遍历时别把 i 和 j 写反,尤其和 vector<vector>></vector> 混用时
原生二维数组是“行主序”,arr[i][j] 表示第 i 行、第 j 列。但有人从 vector<vector>></vector> 过来,习惯先 for (auto& row : vec),再 for (auto& x : row),一换回原生数组就容易把循环顺序搞反,导致逻辑错乱或越界。
典型错误场景:图像处理中按列优先读取像素,却用 for (j) for (i) 遍历 arr[i][j],缓存不友好且结果颠倒。
立即学习“C++免费学习笔记(深入)”;
- 推荐统一用
for (int i = 0; i 外层控行,<code>for (int j = 0; j 内层控列 - 如果真要列优先访问,确保你清楚这是在牺牲局部性;现代 CPU 对行顺序访问有预取优化
- 不要为了“看起来像 vector”而写
for (int i = 0; i —— 下标语义混乱,维护成本高
new int[3][4] 不合法,动态二维数组得用一维模拟或 new int*[3]
C++ 不支持直接 new int[rows][cols](除非 cols 是编译期常量,且语法是 new int[rows][4])。试图写 int (*p)[4] = new int[3][4]; 可行,但 rows 必须已知;若行数也动态,就得自己算偏移。
常见错误现象:int** p = new int*[n]; for (int i = 0; i 看似方便,实际内存不连续,<code>delete[] p 不能释放所有空间,必须逐行 delete[] p[i] 再 delete[] p。
- 更安全的做法:用一维数组模拟二维,
int* p = new int[rows * cols];,访问用p[i * cols + j] - 若坚持用指针数组,务必配对释放:
for (int i = 0; i - 注意:
std::vector<:vector>></:vector>虽灵活,但每行单独分配,不适合大数组或高性能场景
初始化 {} 和 = {} 行为不同,零初始化别漏掉等号
局部变量 int arr[2][3]{}; 会零初始化(全 0),但 int arr[2][3]; 不初始化,内容随机;而 static int arr[2][3]; 即使没写 {} 也会自动零初始化。
最容易被忽略的是:写 int arr[2][3] = {}; 合法且清零,但 int arr[2][3] = {0}; 也清零(C++ 规则:显式初始化首元素为 0,则其余自动补 0),而 int arr[2][3] = {1}; 只设 arr[0][0] == 1,其余仍为 0 —— 这不是 bug,是标准行为。
- 结构体/类中含二维数组成员时,构造函数初始化列表不能直接初始化数组,得靠
= {}或在构造函数体内赋值 - 全局或 static 数组未显式初始化,一定为 0;栈上数组未初始化,就是垃圾值,用前必须确认
- 用
memset(arr, 0, sizeof(arr))也能清零,但仅适用于 POD 类型,且不如= {}直观安全
事情说清了就结束。真正麻烦的从来不是怎么写出来,而是哪一层在管理内存、谁负责释放、下标是不是反着用了——这些地方一错,调试起来比定义本身花十倍时间。











