二维数组名退化为指向行的指针,类型为 int ()[3],非 int 或 int;正确声明为 int (p)[3] = arr;元素地址用 &arri 获取,打印时强制转 void。

二维数组名和指针类型不匹配,直接赋值会编译报错
比如 int arr[2][3],它的类型是 int[2][3],不是 int**,也不是 int*。试图写 int** p = arr 或 int* p = arr 都会触发类型错误(如 cannot convert 'int (*)[3]' to 'int**')。关键在于:二维数组名退化为指向**行**的指针,即 int (*)[3] —— 指向含 3 个 int 的数组的指针。
实操建议:
- 用
int (*p)[3] = arr正确接收数组首地址 - 用
printf("%p", (void*)p)打印该指针值(注意强制转void*) - 别用
%x或%d打印地址,行为未定义
想逐元素看地址,用双重循环加取址运算符 &
二维数组在内存中是连续存储的,arr[i][j] 的地址等于 &arr[0][0] + i * 列数 + j。但最直观、最不易出错的方式是直接对每个元素取地址。
实操建议:
- 外层循环控制行(
i),内层控制列(j) - 统一用
&arr[i][j]获取每个元素地址,避免指针算术误算 - 打印时用
(void*)&arr[i][j]转换,确保可移植 - 示例片段:
for (int i = 0; i
用指针遍历整行时,注意步长是“一行字节数”
如果声明 int (*row)[3] = arr,那么 row + 1 指向第二行起始,地址差正好是 3 * sizeof(int)。这和 int* p = &arr[0][0] 后的 p + 1(只偏移 sizeof(int))有本质区别。
实操建议:
- 用
int (*row)[3]遍历时,row[i]是第i行的整个数组,&(row[i][0])就是该行首地址 - 对比验证:打印
(char*)(row + 1) - (char*)row应等于sizeof(int) * 3 - 若误用
int* p = (int*)arr再做p + 3,虽数值上可能碰巧等于下一行地址,但语义错误且不可靠
不同声明方式导致指针类型完全不同
动态分配的 int** ptr 和栈上声明的 int arr[2][3] 地址布局根本不同:arr 是一块连续内存;ptr 是指针数组,每行单独分配,地址不连续。混用会导致地址打印结果毫无规律,甚至段错误。
实操建议:
- 确认你操作的是哪种二维结构:静态数组、堆上连续分配(如
new int[6])、还是int**分配 - 对静态数组,永远优先用
int (*)[N]类型指针,而不是强行转成int** - 若真要模拟“指针的指针”行为,必须自己管理行首地址数组,不能指望
&arr[0][0]自动变成int**
地址分布本身很直白:按行优先连续排列。真正容易卡住的,是搞不清数组名退化规则、强行套用一维指针思维、以及忽略类型系统对地址运算的约束。打印前多看一眼变量声明和指针类型,比反复试错快得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











