遍历std::vector<int>:引用范围for与下标遍历哪种速度更快?
两种遍历写法的性能对比结论
对于存储int这类内置类型的std::vector<int>实例,在竞技编程默认开启的O2/O3最高优化等级下,两种写法的执行速度完全一致,没有可测量的性能差异,不存在引用范围for比下标遍历更快的情况,反过来也一样。
两种写法的底层实现逻辑
你觉得两种写法逻辑有差异,本质是没看清编译器对两种写法的展开逻辑:
- 按引用的范围for循环是标准语法糖,编译器会直接展开为指针/迭代器遍历:
vector的迭代器在优化模式下就是原生int指针,auto* __begin = v.data(); auto* __end = v.data() + v.size(); for (; __begin != __end; ++__begin) { int& el = *__begin; // 循环内逻辑 }*__begin是直接的指针解引用操作,没有任何额外函数调用或者拷贝开销。 - 下标遍历的
v[i],底层实现就是指针偏移计算:*(v.data() + i),和范围for展开后的内存访问逻辑完全同源,不存在本质区别。
开启最高优化后,编译器会直接消掉所有冗余的中间变量、偏移计算步骤,两种写法最终生成的汇编代码是完全相同的:都是从vector存储的首内存地址开始,每次步进4字节(int的长度),直到读取完所有元素,循环内的元素操作也会被做同等程度的优化。
为什么竞技编程选手普遍用范围for写法
大家选这种写法从来不是因为它更快,纯粹是因为它更不容易出错、写起来效率更高:
- 不需要手动声明下标变量,不用特意写
size_t类型,不会出现循环条件写错(比如把<写成<=)、下标越界这类低级失误 - 代码泛用性更强,如果后续需要把vector换成其他STL容器(比如deque、list),范围for的代码不需要任何修改就能直接用,下标遍历在不支持随机访问的容器上根本无法编译
- 只要写的是
auto&就不会有拷贝开销,性能和手写下标遍历完全打平,写起来还少敲很多字符,自然成为大家的首选。
注意:如果你写的是不带引用的
for (auto el : v)按值遍历,那确实会因为逐个拷贝元素比下标遍历慢,但你提到的写法是带引用的auto&,不存在这个问题。
唯一可能出现速度差异的场景是无优化的debug模式:部分STL实现的下标运算符或者迭代器会带额外的调试检查逻辑,但这类检查在竞技编程的编译选项里是默认全关的,完全不影响最终提交的运行速度。
内容的提问来源于stack exchange,提问作者A. Fenzry
相关产品推荐
相关产品推荐

