严格来说,C++语言中没有多维数组,通常所说的多维数组其实就是数组的数组。
当一个数组的元素仍然是数组时,通常使用两个维度来定义它,一个维度表示数组本身的大小,另一个维度表示其元素(也是数组)的大小。
int ia[3][4]; //大小为3的数组,每个元素都是含有4个整数的数组
int arr[10][20][30]; //大小为10的数组,每个元素都是大小为20的数组,这些数组的元素是含有30个整数的数组
多维数组的初始化
多维数组的每一行分别使用花括号括起来:
int ia[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
也可以不使用花括号标识行:
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
显示地初始化每行的首元素,其它未列出的元素执行默认初始化:
int ia[3][4] = { {0},{4},{8}};
显示的初始化第一行,其它元素执行值初始化:
int ix[3][4] = {0,3,6,9};
多维数组的下标引用
可以使用下标运算符来访问多维数组,此时数组的每一个维度对应一个下标运算符。
两层嵌套的 for
循环来处理多维数组:
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt];
for (size_t i = 0; i != rowCnt; ++i)
{
for (size_t j = 0; j != colCnt; ++j)
ia[i][j] = i * colCnt + j;
}
使用范围for语句来处理多维数组
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt] = {0,1,2,3,4,5,6,7,8,9,10,11};
size_t cnt = 0;
for (auto &row : ia) //对于外层数组的每一个元素
for (auto &col : row) //对于内层数组的每一个元素
col = cnt;
++cnt;
第一个for
循环遍历 ia
的所有元素,这些元素都是大小为4的数组,因此 row
类型就应该是含有4个整数的数组引用。
第二个 for
循环遍历那些4元素数组中的某一个,因此 col
的类型是整数的引用。
上面因为改变了数组的元素,所以选用引用类型作为循环控制变量:
for (auto &row : ia) //对于外层数组的每一个元素
for (auto col : row) //对于内层数组的每一个元素
cout << col << endl;
这个循环并没有写操作,可还是将外层的循环控制变量声明为引用类型,这是为了避免数组被自动转换成指针,如果不使用引用类型,则变成:
for (auto row : ia)
for (auto col : row)
程序无法编译通过,第一个 for
循环遍历 ia
的所有元素,这些元素实际上是大小为4的数组,因为 row
不是引用类型,所以编译器初始化 row
时会自动将这些数组形式的元素转换成指向该数组内首元素的指针。这样 row
就是 int*
类型,显然内层的循环就不合法,编译器试图在 int*
内遍历。
所以,要使用范围 for
语句处理多维数组,除了最内层的循环外,其它所有循环的控制变量都应该是引用类型。
指针和多维数组
当程序使用多维数组的名字的时候,也会自动将其转换成指向数组首元素的指针。
因为多维数组实际上数组的数组,所以由多维数组名转换得来的指针实际上是指向第一个内层数组的指针:
int ia[3][4]; //大小为3的数组,每个元素都是含有4个整数的数组
int (*p)[4] = ia; //p指向含有4个整数的数组
p = &ia[2]; //p指向ia的尾元素
使用 auto
或 decltype
能尽可能避免在数组前面加上一个指针类型。
//p指向含有4个整数的数组
for (auto p = ia; p != ia + 3; ++p)
//q指向4个整数数组的首元素,也就是说q指向一个整数
for (auto q = *p; q != *p + 4; ++q)
cout << *q << endl;
for (auto p = begin(ia); p != end(ia); ++p)
for (auto q = begin(*p); q != end(*p); ++q)
cout << *q << endl;
类型别名简化多维数组的指针
for (int_array *p = ia; p != ia + 3; ++p)
for (int *q = *p; q != *p + 4; ++q)
cout << *q << endl;