a + 1 跳过一整行字节数,即4 sizeof(int)字节;因a退化为int ()[4],步长由第二维大小决定,sizeof(*a)可验证该值。

二维数组名作为指针时,a + 1 跳多少字节?
它跳的不是「一行」的字节数,而是整个「第一维元素」的大小。比如 int a[3][4],a 类型是 int[3][4],但作为右值退化为 int (*)[4](指向含 4 个 int 的数组的指针)。所以 a + 1 实际跳过 4 * sizeof(int) 字节,即一整行。
- 关键看声明:只有
T arr[M][N]这种栈上静态二维数组,arr才能自然退化为T (*)[N] - 若写成
int **p模拟二维,p + 1只跳sizeof(int*),和行长度无关 - 用
sizeof(*a)可直接验证步长:对int a[3][4],sizeof(*a) == 16(假设int为 4 字节)
int (*p)[4] 和 int *p 解引用行为差异
前者解引用得到一个长度为 4 的 int 数组(类型 int[4]),后者解引用只得到单个 int。这意味着下标运算逻辑完全不同:
-
int (*p)[4] = a;→(*p)[0]等价于a[0][0];p[1]是第 1 行首地址(即&a[1][0]) -
int *p = &a[0][0];→p[5]是第 2 行第 2 列(a[1][1]),因为按线性内存算偏移 - 混用容易越界:把
int *p = a;当作指针数组用,p[1]实际是a[0][1],不是你想要的第二行
传参时用 void func(int (*p)[4]) 还是 void func(int p[][4])?
二者完全等价,编译器都视为 int (*)[4]。但必须指定列数(第二维),否则无法计算行内偏移。省略列数如 func(int p[][]) 是非法的。
- 错误写法:
func(int p[][0])、func(int **p)(除非你真传的是指针数组) - 若列数不固定,只能用一维指针加手动计算:
func(int *p, int rows, int cols),访问p[i * cols + j] - 现代替代方案:优先用
std::vector<:vector>></:vector>或std::span<:span>></:span>(C++20),避开裸指针步长陷阱
用 std::array 避免裸指针步长困惑
静态二维场景下,std::array<:array>, 3></:array> 的 data() 返回 int*,而对象本身支持 [i][j],且类型安全。它不涉及指针退化,也就没有「步长理解错」的问题。
-
auto& a = std::array<:array>, 3>{};</:array>→&a[1]类型是std::array<int>*</int>,步长明确为sizeof(std::array<int>)</int> - 对比裸数组:
int a[3][4]的&a[1]类型也是int (*)[4],但容易被误读为int** - 真正难处理的是跨函数传递「列数运行时确定」的二维数据——这时候步长必须靠参数显式携带,别试图靠指针类型推导
实际写 C++ 时,只要看到二维数组指针运算,先盯住「声明里第二维是不是常量表达式」。不是,就别用 T (<em>)[N]</em>;是,就用 sizeof(ptr) 直接确认步长,比心算靠谱。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











