第5.9章: 多维 C 风格数组
概述
多维 C 风格数组就是元素本身也是 C 风格数组的数组。 最常见的形式是二维数组,你可以把它想象成由行和列组成的表格。
在本章中,你将学到:
- 如何声明和初始化多维 C 风格数组
- 它们在内存中如何布局(行主序)
- 索引如何工作
- 多维数组的退化如何工作
- 为什么只有最外层维度会退化
- 涉及哪些指针类型(例如
int (*)[N])
1. 声明多维 C 风格数组
二维数组通过同时指定两个维度来声明:
int a[2][3]{
{1, 2, 3},
{4, 5, 6}
};
这表示:
- 2 行
- 每行 3 列
- 每一行是一个含 3 个
int的数组
2. 内存布局:行主序
C++ 以行主序存储多维数组。 这意味着整个第一行先存储,然后是第二行,依此类推。
int a[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
Memory (addresses vary):
Address: 1000 1004 1008 1012 1016 1020
Memory: [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ]
↑ ↑ ↑ ↑ ↑ ↑
a[0][0] ... ... a[1][2]
尽管数组看起来像一张网格,它其实存储为一个连续的块。
3. 索引多维数组
使用多个 [] 运算符访问元素:
int x = a[0][1]; // 2
int y = a[1][2]; // 6
表达式 a[i] 指第 i 行,它本身
是一个含 3 个 int 的数组。
int b[3] = a[0]; // ❌ error: cannot copy arrays
但你可以取它的地址:
int (*row0)[3] = ::std::addressof(a[0]);
a[0] 的类型是 int[3]。
&a[0] 的类型是 int (*)[3]。
4. 多维数组的退化
与一维数组一样,多维数组在大多数表达式中也会退化——但 只有最外层维度会退化。
int a[2][3];
int (*p)[3] = a; // decay: a → pointer to array of 3 int
重点:
a退化为int (*)[3]a[0]在被用于表达式之前不退化a[0]是一个含 3 个int的数组
这就是为什么:
int *p0 = a[0]; // decay: a[0] → &a[0][0]
而:
int (*p1)[3] = a; // pointer to row
5. 多维数组的指针算术
对 int (*)[3] 做指针算术会按整行移动。
int (*p)[3] = a;
int (*p_next)[3] = p + 1; // moves to a[1]
而对 int* 做指针算术则按单个元素移动。
int *q = a[0]; // points to a[0][0]
int *q2 = q + 4; // points to a[1][1]
这能成立,是因为整个 2×3 数组是连续的。
6. 使用 ::std::ranges::begin 与 ::std::ranges::end
你可以为外层维度获取迭代器:
#include
int a[2][3]{{1,2,3},{4,5,6}};
int (*first)[3] = ::std::ranges::begin(a);
int (*last)[3] = ::std::ranges::end(a);
它们计算的是:
&a[0](对begin(a))&a[0] + 2(对end(a))
它们不会搜索 '\0',因为这不是字符串。
7. 更高维的数组
你可以扩展到任意维度:
int a[2][3][4];
这是一个含 2 个元素的数组,
每个元素是一个含 3 个元素的数组,
每个元素又是一个含 4 个 int 的数组。
内存依然是一整块连续区域。
要点
- 多维 C 风格数组就是数组的数组。
- C++ 以行主序存储多维数组。
a[i]本身就是一个数组。- 对内层维度,
&a[i]的类型是int (*)[N]。 - 只有最外层维度会退化。
a退化为int (*)[N]。a[i]在用于表达式时退化为int*。- 对
int (*)[N]做指针算术按行移动。 - 对
int*做指针算术按单个元素移动。 - 外层维度用
::std::ranges::begin(a)与::std::ranges::end(a)。