C++11 起可以用范围 for 遍历容器、数组或初始化列表,不必手写下标。这种写法在底层就是对 begin 与 end 之间的迭代器做循环,因此本节会同时给出范围 for 和迭代器两种写法,方便对照。迭代器的类别与失效规则见后面的迭代器章节。

基本语法

形式为 for (声明 : 范围表达式) 语句。范围表达式常为数组、std::vector、std::string 等;每次迭代将其中的元素交给循环变量。

C++ 范围 for运行
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {3, 1, 4, 1, 5};
    for (int x : v) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出:

3 1 4 1 5 

循环变量 x 为各元素的拷贝,修改 x 不影响 v。对 int 等小类型开销可忽略;对大型对象应使用引用以避免拷贝。

引用与 const

std::vector<int> v = {1, 2, 3};

for (int& x : v) {
    x *= 2;  // 修改容器内元素,v 变为 2, 4, 6
}

const std::vector<int> frozen = {1, 2};
for (const int& x : frozen) {
    std::cout << x;  // 只读,不可写 x
}

只读遍历大对象时,const auto& 可避免拷贝且禁止修改,适用于 std::string 或自定义结构体(auto 详见auto 与 decltype)。frozen 为 const 时,循环变量不能写成非 const 引用。

循环变量形式拷贝可修改元素
T x是否(改的是副本)
T& x否是(非 const 范围)
const T& x否否

内置数组与花括号列表

int arr[] = {9, 8, 7};
for (int n : arr) {
    std::cout << n << " ";
}

for (int x : {1, 2, 3}) {
    std::cout << x;
}

第一段遍历数组 arr:数组名在范围 for 里仍表示整块数组,因此能走完 9、8、7。若手里只有指向首元素的指针、没有长度,就不能对这个指针单独写范围 for。

第二段没有事先声明数组,{1, 2, 3} 会生成一份临时的初始化列表,类型是 std::initializer_list<int>,范围 for 按这几个值走一遍。这种方式只适合当场列出少量数字,循环结束后这份列表就消失了。

与迭代器的关系

迭代器表示容器里的当前位置:v.begin() 指向第一个元素,v.end() 指向最后一个元素的后面(这个位置没有元素,不能写成 *v.end())。++it 移到下一个元素,*it 取出当前值。范围 for 不会让你手写这些步骤,编译器会按同一套规则展开,因此下面两段对这个例子打印的都是 1020:

std::vector<int> v = {10, 20};

for (int x : v) {
    std::cout << x;
}

for (auto it = v.begin(); it != v.end(); ++it) {
    std::cout << *it;
}

只是依次访问每个元素时,范围 for 更短。需要在循环里比较位置、插入或删除时,就要自己拿着迭代器写。数组没有 begin 成员,范围 for 会用指向首元素和尾后位置的指针来充当迭代器。更完整的说明见迭代器章节。

修改范围时的注意

在范围 for 遍历过程中对同一容器执行 push_back 等可能使迭代器失效,导致未定义行为。若需在循环中增删元素,应使用下标循环、迭代器算法,或先收集修改再统一应用。仅通过引用改当前元素的值,一般不涉及插入删除,不受此限。

本节说明了范围 for 的写法、引用选择与迭代器对应关系。下一章从指针开始,讨论地址、解引用与空指针。