二维数组名是行指针而非双重指针,int arr3中arr类型为int ()[4];用int*遍历时需先构造行首地址数组再赋值。

二维数组名本身就是指向首行的指针
当你声明 int arr[3][4],arr 的类型不是 int**,而是 int (*)[4] —— 即“指向含 4 个 int 的数组的指针”。这是关键前提,错当成 int** 是最常见崩溃根源。
所以用指针遍历,得按“行指针”逻辑走:
-
arr + i指向第i行(类型:int (*)[4]) -
*(arr + i)等价于arr[i],是第i行首地址(类型:int*) -
*(*(arr + i) + j)就是arr[i][j]
用双重指针变量遍历必须显式取地址
如果真想用 int** p 遍历,不能直接赋值 p = arr(类型不匹配,编译报错:cannot convert 'int (*)[4]' to 'int**')。你得自己构造一个“行首地址数组”:
int arr[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int* row_ptrs[3] = {arr[0], arr[1], arr[2]};
int** p = row_ptrs;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
printf("%d ", *(*(p + i) + j)); // 或 p[i][j]
}
}注意:row_ptrs 必须是 int* 数组,不是 int**;p 只是指向这个数组首元素的指针。
立即学习“C++免费学习笔记(深入)”;
用一维指针遍历更简洁且无歧义
把二维数组当连续内存块处理是最安全、最常用的方式。因为 arr[3][4] 在内存中就是 12 个 int 连续排列:
-
int* p = &arr[0][0]或int* p = (int*)arr - 访问
arr[i][j]→*(p + i * 4 + j)(列数 4 是关键!) - 循环时别写成
i * 3 + j(那是按行数算,错)
这种写法兼容所有 C++ 标准,不依赖类型推导,也避免了行指针和二级指针的语义混淆。
auto 和范围 for 更推荐用于现代 C++
如果你只是想遍历并读/写,不用指针反而更清晰:
for (auto& row : arr) {
for (auto& elem : row) {
elem *= 2; // 直接修改
}
}或者用索引:
for (size_t i = 0; i < std::size(arr); ++i) {
for (size_t j = 0; j < std::size(arr[i]); ++j) {
std::cout << arr[i][j] << " ";
}
}手动管理指针容易越界或类型误用,尤其在函数传参时——void f(int a[][4]) 和 void f(int (*a)[4]) 才等价,void f(int** a) 完全不通。
真正要传指针遍历时,列数必须作为常量或模板参数固化,否则无法做指针算术。这点很容易被忽略,直到运行时崩掉才意识到维度信息丢了。


















