在C++范围for循环中向vector追加元素为何输出异常?
范围for循环遍历std::vector时push_back导致异常输出的原因分析
问题代码
auto main() -> int { std::vector<double> v { 1, 2, 3 }; for (auto& num : v) { std::cout << num << "\n"; v.push_back(num); } std::cout << "===========\n"; for (const auto& num : v) { std::cout << num << "\n"; } }
实际运行输出
1 1.00938e-320 0 =========== 1 2 3 1 1.00938e-320 0
预期输出
1 2 3 =========== 1 2 3 1 2 3
疑问
原以为范围for循环是如下代码的语法糖:
for (auto& it{v.begin()}, it != v.end(), it++)
照此逻辑,应该只会出现两种情况:1. 每次迭代都重新计算v.end(),导致无限循环;2. 仅在循环开始前计算一次v.end(),得到预期输出。但实际结果与这两种情况都不符,请问这是为什么?
原因解析
首先纠正你的误解:范围for循环的实际等价逻辑和你想象的不同。根据C++标准,范围for循环会被展开为以下形式:
auto&& __range = v; auto __begin = __range.begin(); auto __end = __range.end(); for (; __begin != __end; ++__begin) { auto& num = *__begin; // 循环体代码 }
核心点是:__begin和__end仅在循环启动前初始化一次,后续迭代不会重新计算。问题的根源在于std::vector::push_back的行为:
当push_back新元素时,如果vector当前容量不足以容纳新增元素,会触发内存重新分配——vector会申请一块更大的内存,把旧数据拷贝过去,然后释放旧内存。此时,所有指向旧内存的迭代器(包括范围for循环中保存的__begin和__end)都会彻底失效。
回到你的代码执行流程:
- 第一次循环:
__begin指向第一个元素1,输出后执行push_back(1)。此时vector的size从3变为4,若初始容量为3(很多实现的默认初始容量等于初始size),则会触发扩容。扩容后,原__begin和__end指向的旧内存已被释放,成为失效迭代器。 - 第二次循环:解引用失效的
__begin属于未定义行为,输出的1.00938e-320是旧内存中的垃圾值。 - 第三次循环:继续移动失效的
__begin,解引用得到另一个垃圾值0。 - 当失效的
__begin与__end比较时,恰好满足__begin != __end不成立,循环终止——这就是为什么循环只执行了3次,却输出异常值。
如果提前给vector预留足够容量(比如在循环前调用v.reserve(6)),push_back不会触发扩容,迭代器也不会失效。此时循环会按你预期的第二种情况执行:遍历初始的3个元素,输出1、2、3,最终vector变为[1,2,3,1,2,3]。
内容的提问来源于stack exchange,提问作者Fei Yu
相关产品推荐
相关产品推荐

