很多时候,我们并不关心数组元素排在第几个下标位置,只想把里面的每个元素从头到尾依次拿出来用一遍。
针对这种场景,C++11 引入了一种更简洁的遍历语法,叫做 “范围 for 循环(range-based for)”。使用它遍历数组时,我们不需要手动定义下标变量,也不需要计算数组长度,代码会清爽很多。
范围 for 循环语法
在 C++ 中,我们可以使用范围 for 循环直接遍历整个数组,自动把里面的元素从头到尾逐个取出来。
语法:
for (元素类型 变量名 : 数组名) {
// 循环体
}说明:
你可以把小括号中间的英文冒号(:)直观地理解成:“从右边这个数组中,按顺序逐个取出元素交给左边的变量”。数组里一共有多少个元素,范围 for 循环就会自动执行多少轮,不需要我们手动控制循环次数。
示例 1:遍历输出数组元素
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40};
for (int num : arr) {
std::cout << num << ' ';
}
return 0;
}运行结果如下。
10 20 30 40分析:
在这个例子中,for (int num : arr) 会从数组 arr 的第 1 个元素开始按顺序遍历。由于 arr 一共有 4 个元素,循环会自动执行 4 轮,变量 num 在每一轮依次拿到 10、20、30、40。等最后一个元素访问完毕,循环就会自动结束。
同样地,如果我们要计算数组中所有元素的总和,使用范围 for 循环写起来也非常直观。
示例 2:计算数组元素之和
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40};
int sum = 0;
for (int num : arr) {
sum += num;
}
std::cout << "元素总和:" << sum;
return 0;
}运行结果如下。
元素总和:100分析:
sum += num; 表示在每一轮循环中,把 num 依次拿到的 10、20、30、40 累加到变量 sum 中,最终输出总和 100。
提示 和普通的 for 循环一样,我们在范围 for 循环内部同样可以使用 break 提前结束整个循环,或者使用 continue 跳过当前元素。
配合 auto 自动推导类型
在编写范围 for 循环时,由于右边数组的元素类型已经是确定的,我们可以配合 auto 关键字,把左边的元素类型直接写成 auto,让编译器自动推导出变量的类型。
语法:
for (auto 变量名 : 数组名) {
// 循环体
}说明:
如果右边是 int 数组,auto 就会自动推导为 int;如果右边是 double 数组,auto 就会自动推导为 double。这样即使后续修改了数组的元素类型,循环头部的代码也不用跟着改动。
示例 3:配合 auto 遍历数组
#include <iostream>
int main() {
double prices[] = {19.9, 9.5, 25.6, 12.8};
for (auto price : prices) {
std::cout << price << ' ';
}
return 0;
}运行结果如下。
19.9 9.5 25.6 12.8分析:
数组 prices 的元素类型是 double,因此在 for (auto price : prices) 中,编译器会自动将 auto 推导为 double 类型,并依次打印出每一个价格。
使用范围 for 修改元素
在使用范围 for 循环时,有一个初学者很容易忽略的细节:如果写成 for (int num : arr),在循环体内部修改 num 的值,并不会改变原数组 arr 里的元素。下面我们来看一个例子。
示例 4:普通变量无法修改原数组
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40};
// 尝试将每个元素加 5
for (int num : arr) {
num += 5;
}
for (int num : arr) {
std::cout << num << ' ';
}
return 0;
}运行结果如下。
10 20 30 40分析:
从运行结果可以看出,虽然我们在第 1 个循环里执行了 num += 5,但最后打印出来的原数组依然是 10、20、30、40,没有任何变化。
为什么会这样呢?因为 for (int num : arr) 在每一轮循环中,只是把原数组里的元素 “复印了一份” 交给临时变量 num。循环体里的 num += 5 修改的只是手里这份副本,原数组 arr 里的原始数据自然不会受到影响。
那如果我们希望通过范围 for 循环直接修改原数组里的元素,应该怎么做呢?方法很简单,只需要在类型后面加上一个 & 符号,写成 int& num 或 auto& num 就可以了。
示例 5:使用引用修改原数组
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40};
// 加上 & 直接修改原元素
for (int& num : arr) {
num += 5;
}
for (int num : arr) {
std::cout << num << ' ';
}
return 0;
}运行结果如下。
15 25 35 45分析:
在上面代码中,int& 用到了 C++ 的引用,后面章节会详细介绍。这里跟在类型名后面的 & 并不是按位与运算符,它的作用是给原数组里的元素起一个 “别名”。
加上 & 之后,变量 num 就直接绑定了原数组中的元素本身,相当于直接操作原件而不再复制副本,因此在循环里修改 num 就会同步改变原数组里的值。
结合前面介绍的 auto 关键字,在实际开发中我们只需要把下面这 2 种常用写法区分清楚就可以了:
// 只读遍历
for (auto num : arr) {
std::cout << num << ' ';
}
// 修改元素
for (auto& num : arr) {
num += 5;
}两种 for 循环如何选择?
学到这里,我们已经掌握了遍历数组的两种常用方式:一种是通过下标控制循环,另一种是使用范围 for 循环直接取出元素。在实际开发中,大家可以根据以下 2 条标准来选择:
- 优先使用范围 for 循环:如果只需要从头到尾按顺序读取或修改每个元素,并且不需要知道当前元素的下标位置,推荐使用范围
for循环。这样代码最简短,还能避免写错循环条件引发下标越界。 - 按需使用传统下标 for 循环:如果在循环中需要用到元素的下标
i、需要倒序遍历,或者只需要遍历数组的一部分元素,则使用传统的下标for循环。
