3.6 多维数组(1)
严格来说,C++(www.cppentry.com)语言中没有多维数组,通常所说的多维数组其实是数组的数组。谨记这一点,对今后理解和使用多维数组大有益处。
当一个数组的元素仍然是数组时,通常使用两个维度来定义它:一个维度表示数组本身大小,另外一个维度表示其元素(也是数组)大小:
- int ia[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组
- // 大小为10的数组,它的每个元素都是大小为20的数组,
- // 这些数组的元素是含有30个整数的数组
- int arr[10][20][30] = {0}; // 将所有元素初始化为0
如3.5.1节(第115页)所介绍的,按照由内而外的顺序阅读此类定义有助于更好地理解其真实含义。在第一条语句中,我们定义的名字是ia,显然ia是一个含有3个元素的数组。接着观察右边发现,ia的元素也有自己的维度,所以ia的元素本身又都是含有4个元素的数组。再观察左边知道,真正存储的元素是整数。因此最后可以明确第一条语句的含义:它定义了一个大小为3的数组,该数组的每个元素都是含有4个整数的数组。
使用同样的方式理解arr的定义。首先arr是一个大小为10的数组,它的每个元素都是大小为20的数组,这些数组的元素又都是含有30个整数的数组。实际上,定义数组时对下标运算符的数量并没有限制,因此只要愿意就可以定义这样一个数组:它的元素还是数组,下一级数组的元素还是数组,再下一级数组的元素还是数组,以此类推。
对于二维数组来说,常把第一个维度称作行,第二个维度称作列。
多维数组的初始化
允许使用花括号括起来的一组值初始化多维数组,这点和普通的数组一样。下面的初始化形式中,多维数组的每一行分别用花括号括了起来:
- int ia[3][4] = { // 三个元素,每个元素都是大小为4的数组
- {0, 1, 2, 3}, // 第1行的初始值
- {4, 5, 6, 7}, // 第2行的初始值
- {8, 9, 10, 11} // 第3行的初始值
- };
其中内层嵌套着的花括号并非必需的,例如下面的初始化语句,形式上更为简洁,完成的功能和上面这段代码完全一样:
- // 没有标识每行的花括号,与之前的初始化语句是等价的
- int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
类似于一维数组,在初始化多维数组时也并非所有元素的值都必须包含在初始化列表之内。如果仅仅想初始化每一行的第一个元素,通过如下的语句即可:
- // 显式地初始化每行的首元素
- int ia[3][4] = {{ 0 }, { 4 }, { 8 }};
其他未列出的元素执行默认值初始化,这个过程和一维数组(参见3.5.1节,第114页)一样。在这种情况下如果再省略掉内层的花括号,结果就大不一样了。下面的代码
- // 显式地初始化第1行,其他元素执行值初始化
- int ix[3][4] = {0, 3, 6, 9};
含义发生了变化,它初始化的是第一行的4个元素,其他元素被初始化为0。
多维数组的下标引用
可以使用下标运算符来访问多维数组的元素,此时数组的每个维度对应一个下标运算符。
如果表达式含有的下标运算符数量和数组的维度一样多,该表达式的结果将是给定类型的元素;反之,如果表达式含有的下标运算符数量比数组的维度小,则表达式的结果将是给定索引处的一个内层数组:
- // 用arr的首元素为ia最后一行的最后一个元素赋值
- ia[2][3] = arr[0][0][0];
- int (&row)[4] = ia[1]; // 把row绑定到ia的第二个4元素数组上
在第一个例子中,对于用到的两个数组来说,表达式提供的下标运算符数量都和它们各自的维度相同。在等号左侧,ia[2]得到数组ia的最后一行,此时返回的是表示ia最后一行的那个一维数组而非任何实际元素;对这个一维数组再取下标,得到编号为[3]的元素,也就是这一行的最后一个元素。
类似的,等号右侧的运算对象包含3个维度。首先通过索引0得到最外层的数组,它是一个大小为20的(多维)数组;接着获取这20个元素数组的第一个元素,得到一个大小为30的一维数组;最后再取出其中的第一个元素。
在第二个例子中,把row定义成一个含有4个整数的数组的引用,然后将其绑定到ia的第2行。
再举一个例子,程序中经常会用到两层嵌套的for循环来处理多维数组的元素:
- constexpr size_t rowCnt = 3, colCnt = 4;
- int ia[rowCnt][colCnt]; // 12 个未初始化的元素
- // 对于每一行
- for (size_t i = 0; i != rowCnt; ++i) {
- //对于行内的每一列
- for (size_t j = 0; j != colCnt; ++j) {
- // 将元素的位置索引作为它的值
- ia[i][j] = i * colCnt + j;
- }
- }
外层的for循环遍历ia的所有元素,注意这里的元素是一维数组;内层的for循环则遍历那些一维数组的整数元素。此例中,我们将元素的值设为该元素在整个数组中的序号。
使用范围 for语句处理多维数组
由于在C++(www.cppentry.com)11新标准中新增了范围for语句,所以前一个程序可以简化为如下形式:
- size_t cnt = 0;
- for (auto &row : ia) // 对于外层数组的每一个元素
- for (auto &col : row) { // 对于内层数组的每一个元素
- col = cnt; // 将下一个值赋给该元素
- ++cnt; // 将 cnt加1
- }