为了讲清楚我们要说明的问题,首先我们来定义一个二维数组:

int ival[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

这是一个2行3列的二维数组,如果我们要使用范围 for 循环来输出这个二维数组中的元素的话,相应地代码如下:

for (auto &row : ival) {
    for (auto elem : row)
        cout << elem << " ";
    cout << endl;
}

上面的代码能不能完成预期的输出二维数组元素的功能呢?我们来实际运行一下,在 VS2013 中的代码如下:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_范围for循环

运行结果如下:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_引用操作符_02

for (auto row : ival)for (auto &row : ival)

for (auto mbeg = begin(ival), mend = end(ival);
    mbeg != mend; ++mbeg)
    auto &row = *mbeg;

在 VS2013 中运行一下看看改写的是否正确:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_二维数组_03

运行结果如下:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_引用操作符_02

显然我们改写的是对的,实际上,范围 for 循环本质上就是我们改写的传统 for 循环,下面就我们改写后的传统 for 循环来分析一下:

begin(ival) 得到的是指向 ival 首元素的指针,ival 的首元素是什么呢?二维数组本质上是数组的数组,所以我们得到的是一个指向含有 3 个元素的一维数组的指针,指向数组的指针实际上是指向数组首元素的指针的指针(见我的博文: 指向数组的指针与指向数组首元素的指针auto row = *mbeg;int*,即 row 是一个整型指针的副本,接下来的范围 for 语句就成了遍历一个指针内的元素了,这显然是错误的,与我们的初衷大相径庭,但是如果我们加上一个引用操作符( & ),对 mbeg 解引用之后得到的指针相当于一个一维数组的数组名,因此 row 的类型就变成了 int (&row)[3],即 row 是与一个含有 3 个整型数的一维数组绑定的引用,接下来的范围 for 语句就是遍历这个含有 3 个整型数的一维数组,这与我们的初衷是相符的,实际上:

要使用范围 for 语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型

指针的类型包括指针所指向的类型

还有一个极易混淆的地方需要特别指出一下,在我们处理 initializer_list 类型的元素的时候,通常也会用到 & 操作符:

// 打印 list 中的元素
void print (initializer_list<string> list)
{
    for (const auto &elem : list)
        cout << elem << " ";
    cout << endl;
}

这里的范围 for 语句为什么要用到引用操作符呢?实际上,不用引用操作符也是可以的,也就是说,这里的范围 for 语句可以写成如下的形式:

for (const auto elem : list)

这样最后仍然能够得到一样的结果,这里使用 & 操作符主要是为了避免存储空间的浪费和拷贝造成的时间效率低下而直接将 elem 绑定到一个 const string 类型的对象上(initializer_list 类型的对象里面的元素都是常量),如果不使用 & 操作符的话, elem 将是 list 里面元素的拷贝,造成了存储空间的浪费,下面将程序在 VS2013 中运行一下以验证上面的想法。

加了 & 操作符的情形:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_引用操作符_05

运行结果如下:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_二维数组_06

未加 & 操作符的情形:


python用for循环输出二维数组中的一维数组 for循环输入二维数组_c++_07

运行结果如下:

python用for循环输出二维数组中的一维数组 for循环输入二维数组_二维数组_06

完全符合预期的想法。