多维数组指针
看完你能亲手画出任意维度数组的内存布局并解释指针运算
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
多维数组指针
看完你能亲手画出任意维度数组的内存布局并解释指针运算
一维数组的内存回顾
连续内存、元素地址、数组名本质
二维数组的内存布局
左侧为二维逻辑视图,右侧为物理上的连续内存,展示行优先拼接与地址公式。
多维数组的线性存储
从三维逻辑结构出发,经行优先偏移公式计算,最终摊平为连续的一维内存。
行优先 vs 列优先
行优先和列优先名字像、规则反,混用时偏移计算直接错位。
- 先存完第 0 行的所有元素,再接第 1 行
- a[i][j] 偏移 = i × 列数 + j
- 遍历时外层走行、内层走列更缓存友好
- 先存完第 0 列的所有元素,再接第 1 列
- a(i,j) 偏移 = j × 行数 + i
- 遍历时外层走列、内层走行更缓存友好
数组名作为指针常量
前页我们看到二维数组在内存里也是按行线性排开的,那 arr 这个名字本身究竟指向哪里?它跟一维数组里的 arr 含义完全一样吗?
楼号对首行(一整层),门牌号对单户;跳一号跳整行
多维数组指针表达式的等价变形
从 a[i][j] 出发,逐步拆解下标的指针本质,得到纯指针运算形式。
数组名 vs 指针变量
值相同不代表类型相同——a 是常量、p 是变量,混淆就会越界或编译报错。
- 本质是常量指针,地址固定不变
- 不可赋值:a++、a=p 都非法
- sizeof(a) 得整个数组的字节数
- 本质是存储地址的变量,值可变
- 可赋值:p++、p=a 都合法
- sizeof(p) 得指针本身的大小
验证 a 与 &a[0] 的关系
用一段 C 程序把 a 与 &a[0] 打印出来,对比它们在地址值与步长上的关系。
a 与 &a[0] 值相同,但 a+1 跳一整行(16 字节),*a+1 只跳一个 int(4 字节)——这是行优先遍历的指针基础。
数组指针的本质
上一页我们看到 a 和 &a 的值相同,C 却用两种类型去描述它们——看似多余,恰恰是数组指针区别于普通指针的关键。
车票写明每排4座,类型印着 [4],步长直接由此决定
数组指针的声明与使用
让 p 指向二维数组的“行”,验证步长与三种访问写法。
高亮行串起核心:括号决定 p 是“行指针”,步长 = sizeof(int[3]) = 12;(*p)[i]、p[0][i]、(*(p+1))[i] 三种写法等价。
数组指针 vs 指针数组
两个长得像、优先级相反的概念,括号位置决定一切。
- 本质是指针:指向含 n 个元素的整数组
- 优先级:() 优先 → p 先与 * 结合
- 步长 = n×sizeof(int),p+1 跳一整行
- 本质是数组:含 n 个元素,每个是 int*
- 优先级:[] 优先于 * → p 先与 [] 结合
- 步长 = sizeof(int*),p+1 跳一个指针
二维数组参数的三种声明形式
三种声明在编译器眼里究竟是不是一回事?跑一下就知道。
写法 1 在参数调整后与写法 2 类型完全相同;写法 3 退化为 int*,行列维度信息丢失,只能手动算偏移。
为什么需要指向数组的指针
上一节我们看了三种写法都能接收二维数组:int a[][5]、int (*a)[5]、甚至二维指针。但写法不同,生成的机器码真的相同吗?答案是——形参写什么,编译器就怎么算地址。
不告诉前台每层几间房,305 既是 3 楼 05 号,也是 30 楼 5 号
指向数组的指针作为形参
完整演示 int (*p)[col] 作形参:p+1 跳整行,p[i][j] 两层解引用。
形参 int (*p)[3] 是指向含 3 个 int 数组的指针;p+1 跨 12 字节(一整行);p[i][j] 即 *(*(p+i)+j);调用时 a 自动退化为该类型。
二维数组函数参数传递的内存视图
从调用现场到形参内部,看传递瞬间的地址与维度信息取舍。
二维数组的指针算术规则
把 a[i][j] 拆成纯指针运算,一步步看清每步在干什么。
用指针遍历二维数组
对比双重下标与纯指针算术两种遍历二维数组的方式,并验证它们访问同一块内存。
L5 取首元素地址;L10/12 双重循环按下标访问;L17/18 单层循环按指针偏移访问;L22 证明 a[1][2] 与 *(p+5) 指向同一地址。
常见错误与边界情况
三类错误症状都像「指针乱跑」,但病在边界、类型、步长三个不同维度。
- p++ 一路扫到末尾,就以为越界
- int* p 当 int(*)[N] 用,步长不一致
- p += 1 在不同类型上移的距离不同
- 越界看类型步长,分清越过哪一维
- 类型决定步长,混用必然错位
- 一维用 T*,二维用 T(*)[N]
多维数组指针要点回顾
- ✓内存本质是一维连续的线性空间
- ✓指针类型决定了 +1 跨越的元素数
- ✓下标访问等价于指针的解引用
- ✓传参时高维信息会部分丢失
- ✓类型不匹配是越界访问的常见根因
课后思考
先自己琢磨再看参考答案,三道题对应核心、用法与边界。
参考答案圆括号决定 `*` 与 `p` 哪个先结合。`int (*p)[4]` 是指向含 4 个 int 数组的指针;`int *p[4]` 是装着 4 个 int 指针的数组——一个是指针,一个是数组。
参考答案`a` 退化为 `int (*)[5]`,列数不匹配通常报错;`a[0]` 退化为 `int*`,类型完全不符,必报错;`&a[0][0]` 也是 `int*`,同样不匹配。
参考答案多数编译器会警告不匹配。强行运行后 `*p` 按 4 列步进访问,列偏移错位,越界踩到临近内存,结果难料。