一般认为C++中数组只有一维,高维数组如int ia[3][4]
其实是由三个包含四个int
型元素的一维数组元素组成的,即int ia[3][4]
含有三个元素,每个元素都是一个int[4]
类型的数组。
在阅读《C++ Primer》的过程中发现了以下几种遍历“高维”数组的方法,在这里记录下来。
所遍历的二维数组初始化如下:
int ia[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};//列表初始化,还有许多等价的写法
文末附有所有九种访问方式的测试代码。
范围for语句不同于一般的for循环,代码如下:
for (int(&row)[4] : ia) //此处必须加&,因为ia存储的是一个地址
for (int col : row) cout << col << " ";
cout << endl;
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
cout << ia[i][j] << " ";
}
cout << endl;
}
一种常规遍历数组的方法,这里size_t
可直接视为unsigned int
,取值非负。
for (int(*row)[4] = ia; row != ia + 3; row++)
for (int* col = *row; col != *row + 4; col++)
cout << *col << " ";
cout << endl;
这里用到的row
和col
都是指针,所以最后访问具体元素时需要用到解引用符*
,通过*col
遍历数组中的元素。
此外,迭代器的用法和这里的指针非常相似,《C++ Primer》中比较推荐C++程序员采用迭代器的方式去访问元素,因为指针若处理不好可能会有意想不到的效果。这里用row != ia + 3
作为条件而不用row <= ia + 3
作为终止条件也是一个比较有趣的地方,《C++ Primer》推荐C++程序员这样处理。
using int_array = int[4];
typedef int int_array[4]; //这一句和上面一句是等价的
for (int_array& row : ia) { //若不加&会报错:
for (int col : row) {//这句不用加//int *" 类型的值不能用于初始化 "int_array" 类型的实体
cout << col << " ";
}
cout << endl;
}
别名的两种定义方式是一个效果(这里using的定义方式似乎是C++ 11标准的特性),这样其实我们也可以通过两者等价知道int_array
的类型是int[4]
,这里的row
本质上还是一个指针。
using int_array = int[4];
typedef int int_array[4]; //这一句和上面一句是等价的
for (int_array* p = ia; p != ia + 3; p++) {
for (int* q = *p; q != *p + 4; q++) { //注意p要加*才能解引用成指针类型
cout << *q << " ";
}
cout << endl;
}
在这里我们可以看到*p
实际上才是一个数组的首地址,而int* q
表明q
是一个int *
型的指针,通过q
的自增操作,内存中q
移动sizeof(int)
个单位从而达到遍历int[4]
数组中的每一个元素的目的。
for (auto& row : ia) {
for (auto col : row) {
cout << col << " ";
}
cout << endl;
}
此处的&
仍然是必须的,否则row
类型会被视为int *
导致出错。此处的col
是int
型,因此可以直接输出不用解引用。
for (auto i = 0; i != 3; i++) {
for (auto j = 0; j != 4; j++) {
cout << ia[i][j] << " ";
}
cout << endl;
}
很基础的访问方式,i
和j
都属int
型。
for (auto row = ia; row != ia + 3; row++) {
for (auto col = *row; col != *row + 4; col++) {
cout << *col << " ";
}
cout << endl;
}
这实际上就是第三种情况的傻瓜版,auto
,yyds。
#include //使用begin和end函数需要加上这个头文件和相应的using声明
//p指向ia的第一个数组
for (auto p = begin(ia); p != end(ia); ++p) {
//q指向内层数组的首元素
for (auto q = begin(*p); q != end(*p); ++q) {
cout << *q << " "; //输出q所指的整数值
}
cout << endl;
}
begin(ia)
会指向ia
的首地址,end(ia)
指向ia
尾地址的下一个地址,这两个函数定义在iterator
头文件中。
#include
#include
#include //使用begin和end函数需要加上这个头文件和相应的using声明
#include //使用setw函数
using std::cin; using std::cout; using std::endl;
using std::string;
using std::begin; using std::end;
using std::setw; //使用命名空间中设置宽度方式的名字
using int_array = int[4];
typedef int int_array[4]; //这一句和上面一句是等价的
typedef int VVINT[2][3];
int main() {
VVINT a{ 0 };
for (int(&row)[3] : a) {
for (int col : row) cout << col << " ";
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "1、以上输出初始化的int[2][3]类型数组"
<< string(10, '-') << endl << endl;
int ia[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
size_t cnt = 0;
//版本1:使用范围for语句管理迭代过程
for (int(&row)[4] : ia)
for (int col : row) cout << col << " ";
cout << endl;
cout << string(10, '-') << setw(70)
<< "2、以上通过范围for语句在同一行输出ia[3][4]"
<< string(10, '-') << endl << endl;
memset(ia, 0, sizeof(ia));
//版本2,普通for语句,下标运算符访问
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
cout << ia[i][j] << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "3、以上通过普通for语句+下标运算符分行输出memset全0后的ia[3][4]"
<< string(10, '-') << endl << endl;
//以下再次初始化ia[3][4]
for (auto& row : ia) {
for (auto& col : row) {
col = cnt++;
}
}
//版本3,普通for语句,指针访问
for (int(*row)[4] = ia; row != ia + 3; row++)
for (int* col = *row; col != *row + 4; col++)
cout << *col << " ";
cout << endl;
cout << string(10, '-') << setw(70)
<< "4、以上通过普通for语句+指针访问在同一行输出再次初始化后的ia[3][4]"
<< string(10, '-') << endl << endl;
//版本4:类型别名+范围for循环
for (int_array& row : ia) { //若不加&会报错:
for (int col : row) {//这句不用加 //int *" 类型的值不能用于初始化 "int_array" 类型的实体
cout << col << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "5、以上通过类型别名+范围for循环分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
//版本5:类型别名+指针访问
for (int_array* p = ia; p != ia + 3; p++) {
for (int* q = *p; q != *p + 4; q++) { //注意p要加*才能解引用成指针类型
cout << *q << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "6、以上通过类型别名+指针访问分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
//版本6:auto+范围for循环
for (auto& row : ia) {
for (auto col : row) {
cout << col << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "7、以上通过auto关键字+范围for循环分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
//版本7:auto+下标访问
for (auto i = 0; i != 3; i++) {
for (auto j = 0; j != 4; j++) {
cout << ia[i][j] << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "8、以上通过auto关键字+下标访问分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
//版本8:auto+指针访问
for (auto row = ia; row != ia + 3; row++) {
for (auto col = *row; col != *row + 4; col++) {
cout << *col << " ";
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "9、以上通过auto关键字+指针访问分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
//版本9:标准库函数begin和end访问
//p指向ia的第一个数组
for (auto p = begin(ia); p != end(ia); ++p) {
//q指向内层数组的首元素
for (auto q = begin(*p); q != end(*p); ++q) {
cout << *q << " "; //输出q所指的整数值
}
cout << endl;
}
cout << string(10, '-') << setw(70)
<< "10、以上通过标准库函数begin和end分行输出ia[3][4]"
<< string(10, '-') << endl << endl;
return 0;
}