🏠 三维数组内存大冒险
一道题引出的知识:指针、数组名、存储顺序,一次讲明白!
📋 目录
- 先来看这道题
- 数组是什么?——储物柜的比喻
- 一维、二维、三维数组——从抽屉到大楼
- 存储顺序——C 语言的"数数规则"
- 数组名和指针——它们到底是什么关系
- 地址计算公式——学会自己算
- 回到题目——一步步解开答案
- 小练习——你来试试看
- 知识小结卡片
1. 先来看这道题
题目:一个三维数组定义为
long long array[6][6][6];,则array[1][2][3]和array[3][2][1]在内存中的位置相差多少字节?
先别急着算!这道题背后藏着三个超级重要的知识点:
- 🧭 存储顺序:多维数组在内存里是怎么排队的?
- 🏷️ 数组名:
array这个名字到底代表什么? - 🔗 指针:地址和指针有什么关系?
搞懂这三个,类似的题目就再也难不倒你啦!
2. 数组是什么?储物柜的比喻
想象你有一排储物柜,每个柜子都有一个编号:
┌───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │
└───┴───┴───┴───┴───┘
- 每个柜子能放一个数据
- 柜子从 0 开始编号(这是 C 语言的规矩,记住哦!)
- 这一排柜子,就是一个数组
写成代码就是:
int a[5]; // 一排 5 个柜子,编号 0~4
💡 小知识:每个柜子有多大?
不同类型的数据,占的"柜子大小"不一样:
| 类型 | 占多少字节 | 比喻 |
|---|---|---|
char |
1 字节 | 一个小抽屉 |
int |
4 字节 | 一个中等箱子 |
long long |
8 字节 | 一个大行李箱 |
字节(Byte)是电脑里计量空间的基本单位,就像我们用"立方米"量房间大小一样。
3. 一维、二维、三维数组——从抽屉到大楼
一维数组:一排抽屉
int a[5];
就像一排 5 个抽屉,从左到右编号 0、1、2、3、4。
二维数组:一个书架
int b[3][4]; // 3 行 4 列
想象一个书架,有 3 层,每层有 4 格:
第0层: [0][0] [0][1] [0][2] [0][3]
第1层: [1][0] [1][1] [1][2] [1][3]
第2层: [2][0] [2][1] [2][2] [2][3]
第一个下标是第几层(行),第二个下标是第几格(列)。
三维数组:一栋大楼!
long long array[6][6][6];
这就像一栋 6 层的大楼,每层有 6 个房间,每个房间里有 6 个柜子!
array[楼层][房间][柜子]
↓ ↓ ↓
6层 6间 6柜
array[0][0][0]:第 0 层,第 0 间,第 0 个柜子array[1][2][3]:第 1 层,第 2 间,第 3 个柜子array[3][2][1]:第 3 层,第 2 间,第 1 个柜子
🎯 记住:下标都是从 0 开始的!所以
[6][6][6]表示每个维度有 6 个元素,编号是 0~5。
4. 存储顺序——C 语言的"数数规则"
电脑的内存其实是一条长长的直线,就像一条无限长的传送带:
→ → → 内存地址从小到大 → → →
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]...
那三维数组这么"立体"的东西,怎么放进一条直线里呢?
答案:行优先存储(Row-major order)
C 语言规定:最右边的下标变化最快。
就像数数一样:
- 先数完一个房间里的所有柜子
- 再数下一个房间
- 一层的房间都数完了,再上一层
举个例子:int a[2][2][2] 的存储顺序
a[0][0][0] ← 第1个
a[0][0][1] ← 第2个 (最右边+1)
a[0][1][0] ← 第3个 (中间+1,右边归零)
a[0][1][1] ← 第4个
a[1][0][0] ← 第5个 (最左边+1,后面都归零)
a[1][0][1] ← 第6个
a[1][1][0] ← 第7个
a[1][1][1] ← 第8个
🎵 记忆口诀
从右往左,满员进位!
就像我们数数:00, 01, 02, ..., 09, 10, 11...
个位满了进十位,十位满了进百位。
数组下标也是一样,最右边先变,满了就往左边进一位!
5. 数组名和指针——它们到底是什么关系
数组名是什么?
int a[5];
这里的 a 是什么?它是数组的名字。
但在 C 语言里,数组名有一个神奇的特性:
大多数情况下,数组名会自动变成"指向数组第一个元素的指针"。
也就是说,a 就等于 &a[0],也就是第0个元素的地址。
数组 a:
┌───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │
└───┴───┴───┴───┴───┘
↑
a 指向这里(第0个元素的地址)
什么是指针?
指针就是地址,就像门牌号一样。
- 每个内存格子都有一个唯一的"门牌号"(地址)
- 指针变量就是用来存这个门牌号的
- 知道了门牌号,就能找到对应的格子
int a[5] = {10, 20, 30, 40, 50};
int *p = a; // p 指向 a 的第0个元素
printf("%d", *p); // 输出 10(p 指向的格子里的值)
printf("%d", *(p+1)); // 输出 20(往后挪一格)
指针加减的秘密
指针 +1,不是地址数字 +1,而是往后挪一个元素的大小!
int *p; // p+1 跳过 4 字节(一个 int 的大小)
char *q; // q+1 跳过 1 字节(一个 char 的大小)
long long *r; // r+1 跳过 8 字节(一个 long long 的大小)
🎯 这就是为什么我们要知道数据类型占多少字节!
多维数组的数组名
对于 long long array[6][6][6]:
| 表达式 | 含义 | 类型 |
|---|---|---|
array |
指向第 0 个二维数组的指针 | long long (*)[6][6] |
array[1] |
指向第 1 层第 0 个一维数组的指针 | long long (*)[6] |
array[1][2] |
指向第 1 层第 2 间第 0 个元素的指针 | long long * |
array[1][2][3] |
具体的元素值 | long long |
一层一层"剥洋葱",每剥一层,指针就更"具体"一点!
6. 地址计算公式——学会自己算
一维数组
T a[N];
a[i] 的地址 = a 的起始地址 + i × sizeof(T)
二维数组
T a[M][N];
a[i][j] 的地址 = 起始地址 + (i × N + j) × sizeof(T)
三维数组(重点!)
T a[D1][D2][D3];
a[i][j][k] 的地址 = 起始地址 + (i × D2 × D3 + j × D3 + k) × sizeof(T)
🧮 公式拆解
把 a[i][j][k] 想象成要数到第几个元素:
- 前面有 i 整层,每层有
D2 × D3个元素 →i × D2 × D3 - 当前层前面有 j 整行,每行有
D3个元素 →j × D3 - 当前行前面有 k 个元素 →
k - 加起来就是"这是第几个元素"
- 再乘以每个元素的大小
sizeof(T),就是字节数啦!
7. 回到题目——一步步解开答案
题目回顾
long long array[6][6][6];
求 array[1][2][3] 和 array[3][2][1] 相差多少字节。
第一步:确定已知条件
- 数据类型:
long long→ 每个元素占 8 字节 - 数组维度:
[6][6][6]→ D1=6, D2=6, D3=6 - 元素 A:
array[1][2][3]→ i=1, j=2, k=3 - 元素 B:
array[3][2][1]→ i=3, j=2, k=1
第二步:分别算两个元素是"第几个"
元素 A:array[1][2][3]
序号 = i × D2 × D3 + j × D3 + k
= 1 × 6 × 6 + 2 × 6 + 3
= 36 + 12 + 3
= 51
所以 array[1][2][3] 是第 51 号元素(从 0 开始数)。
元素 B:array[3][2][1]
序号 = 3 × 6 × 6 + 2 × 6 + 1
= 108 + 12 + 1
= 121
所以 array[3][2][1] 是第 121 号元素。
第三步:算相差多少个元素
相差的元素数 = 121 - 51 = 70 个
第四步:换算成字节
相差字节数 = 70 × 8 = 560 字节
✅ 最终答案
array[1][2][3] 和 array[3][2][1] 相差 560 字节。
🔍 更聪明的算法:直接算下标差
其实不用分别算两个元素的序号,可以直接算下标之差:
序号差 = (3-1) × 6 × 6 + (2-2) × 6 + (1-3)
= 2 × 36 + 0 × 6 + (-2)
= 72 + 0 - 2
= 70
字节差 = 70 × 8 = 560
结果一样!是不是更快?
⚠️ 注意:中间的
j相同(都是 2),所以那一项是 0。但k是 1-3=-2,可别忘了这个负数哦!
8. 小练习——你来试试看
练习 1(基础)
int a[10];
a[3] 和 a[7] 相差多少字节?
👆 点击查看答案
相差元素数 = 7 - 3 = 4
int 占 4 字节
字节差 = 4 × 4 = 16 字节
练习 2(二维)
double b[5][5];
b[1][2] 和 b[3][4] 相差多少字节?(提示:double 占 8 字节)
👆 点击查看答案
序号差 = (3-1) × 5 + (4-2) = 2×5 + 2 = 12
字节差 = 12 × 8 = 96 字节
练习 3(三维,挑战题)
char c[4][5][6];
c[2][3][4] 和 c[0][1][2] 相差多少字节?(提示:char 占 1 字节,注意谁大谁小!)
👆 点击查看答案
c[2][3][4] 序号 = 2×5×6 + 3×6 + 4 = 60 + 18 + 4 = 82
c[0][1][2] 序号 = 0×5×6 + 1×6 + 2 = 0 + 6 + 2 = 8
相差 = 82 - 8 = 74 个元素
char 占 1 字节
字节差 = 74 × 1 = 74 字节
9. 知识小结卡片
🎴 卡片 1:存储顺序
- C 语言用行优先存储
- 最右边的下标变化最快
- 口诀:从右往左,满了进位
🎴 卡片 2:地址公式
三维数组 T a[D1][D2][D3]:
a[i][j][k] 偏移 = (i×D2×D3 + j×D3 + k) × sizeof(T)
🎴 卡片 3:数组名与指针
- 数组名在大多数情况下 = 指向第一个元素的指针
- 指针 +1 = 跳过一个元素的大小(不是 1 字节!)
- 多维数组名"剥一层",指针类型就更具体一层
🎴 卡片 4:常见类型大小
| 类型 | 字节数 |
|---|---|
char |
1 |
int |
4 |
float |
4 |
double |
8 |
long long |
8 |
🎴 卡片 5:解题步骤
- 看清楚数据类型,确定每个元素占几字节
- 看清楚数组维度(D1, D2, D3)
- 用公式算两个元素的序号差
- 序号差 × 每个元素的字节数 = 答案
🎉 恭喜你!
你已经掌握了三维数组内存布局的核心知识。下次再遇到类似的题目,按照我们的步骤一步步来,一定能做对!
记住公式,理解原理,多做练习,你就是内存小达人! 💪

浙公网安备 33010602011771号