C++多维数组
本文涉及:
- 多维数组
constexpr
关键字size_t
关键字- 基于范围的
for
循环 - 标准库
begin()、end()
函数
C++语言并没有多维数组,而我们常说的多维数组,准确的说是数组的数组。
1 | int ia[3][4]; |
对于复杂的表达式,更好的理解方式是从里向外的理解,int ia[3][4]
声明了一个名为ia
的有3个元素的数组,元素是含有4个int
型元素的数组。有点绕,分解一下理解,ia
是一个数组,里面的元素是整形数组。arr
是有3个元素的数组,这3个元素是有4个元素的数组,这个数组的元素是有5个元素的整形数组,即说明arr
是60个int
型对象的集合。
基于下标访问多维数组
可以使用下标运算符访问多维数组的元素,不同维度的下标,代表着不同数组。
当下标的维度等于数组的维度,访问的对象就是给定类型的对象;下表维度低于数组维度时,访问对象就是对应维度索引指向的内层数组。
1 | ia[2][3] = arr[0][0][0]; |
在第一个例子中,对于用到的两个数组来说,表达式提供的下标运算符数量都和它们各自的维度相同。在等号左侧,ia[2]
得到数组ia
的最后一行,此时返回的是表示ia
最后一行的那个一维数组而非任何实际元素;对这个一维数组再取下标,得到编号为[3]的元素,也就是这一 行的最后一个元素。类似的,等号右侧的运算对象包含3个维度。首先通过索引0得到最外层的数组,它是一个大小为3的(多维)数组;接着获取这4个元素数组的第一个元素,得到一个大小为4的一维数组;最后再取出其中的第一个元素。
在第二个例子中,把row
定义成一个含有5个整数的数组的引用,然后将其绑定到arr
的最外层数组第3行元素的第4个对象上。
对于数组的遍历,更多的用到循环语句,多维数组的遍历通常使用for
循环嵌套访问。
1 | constexpr size_t rCnt = 3,cCnt = 4; //使用了constexpr和size_t关键字 |
size_t
是与机器相关的无符号整数,设计的足够大,用来表示数组的下标。
constexpr
关键字表明,声明的对象是一个常量,在编译阶段就能得到值。
使用基于范围的for循环
C++11新标准引入了基于范围的for
循环,配合auto
关键字,可以不必在意多维数组的类型,通过对象引用遍历元素。
1 | //test02 |
使用基于范围的for
循环,使用对象的引用,是有着多重考虑的。如果这样定义for(auto row : a)
循环体,这是row
会被编译器推断为指针类型int *
,此时访问的是大小为4的数组。因为row
不是引用类型,编译器无法在int *
内部遍历,auto col : row
也就不合法了。
值得注意的是,使用基于范围的for
循环处理多维数组是,除了最内层的循环,其他循环的控制变量应该均为引用类型。
1 | for(auto &row : a){ |
上述表达也是同样的效果,对于不改变对象状态的参数传递或者控制变量,使用const
关键字限制,是一个好习惯,即const auto &row : a
。
指针和多维数组
使用数组名时,会自动的转换为一个指向数组首元素的指针,多维数组也是这样。理解多维数组时,要更注意一点,多维数组是数组的数组。
1 | int ia[3][4]; |
C++11新标准的引入auto
关键字,编译器可以根据表达式推断出返回的类型。
1 | //test03 |
使用了auto
关键字,可以不写明指针类型,编译器根据数组名推断出类型。
1 | //test04 |
当然,使用标准库提供的begin()、end()
函数的话,上述代码可以更简洁。
1 | //test05 |
begin()
函数返回指向数组名首元素的指针;end()
函数返回指向尾元素下一位置的指针,这个位置可以作为哨兵节点,通过判断当前位置和哨兵节点位置,来控制循环结束条件。这两个函数定义在iterator
头文件。
还要说明的是,上说例子中,循环结束的条件都是与哨兵节点比较(准确说是比对或判等,而比较倾向于大小比较),而不是使用<=
。这是因为,STL
中的容器实现时,采用的是泛型编程的思想,构建都是类模板,实例化之前,并不能确定所有对象类型都支持大小比较。这里与标准库实现统一,都是使用比对!=
的方式,控制循环。