C++ list调用emplace未更新迭代器解引用得到3而非0的原因
现象原因
这个现象的核心是对std::list迭代器规则、emplace接口语义理解偏差触发的未定义行为,具体流程如下:
- 空list的
begin()和end()返回的是同一个迭代器,指向list内部用来标记链表末尾的尾后哨兵节点,这个节点不存储用户插入的有效数据,标准明确规定不允许对其解引用。你最开始声明的it从初始化完成开始,就一直指向这个哨兵节点。 std::list::emplace(pos, args)的插入规则是:在pos指向的位置之前构造新元素,接口返回值是指向刚插入的新元素的迭代器。你的代码里三次调用emplace都注释掉了将返回值重新赋值给it的逻辑,因此it的指向从来没有发生过变化,始终停在最初的哨兵节点位置。- 三次插入都传入了指向哨兵节点的
it作为位置参数,因此三个元素会依次被插入到哨兵节点之前,最终链表有效元素的顺序就是0 → 1 → 2,这和范围for遍历的输出完全匹配,这部分逻辑是符合预期的。 - 最后执行
*it时,你解引用的是一直指向哨兵节点的尾后迭代器,属于标准规定的未定义行为。绝大多数STL实现中list采用双向循环链表结构,哨兵节点的内存空间没有初始化有效的int值,你读到的3是这块内存里残留的无意义垃圾数据——这个结果完全不可预测,换编译选项、换编译器、调整周边代码,都可能让这里输出其他任意值,甚至直接触发程序崩溃。
补充说明:
std::list的插入操作确实不会导致任何已有迭代器失效,这里的it从来没有失效过,它只是从始至终都没指向过你插入的任何一个有效元素。
复现代码整理如下:
#include <list> #include <iostream> using namespace std; int main() { list<int> l; auto it = l.begin(); // it绑定空list的尾后哨兵节点 l.emplace(it, 0); // 哨兵节点前插入0,it指向不变 /*it =*/ l.emplace(it, 1); // 哨兵节点前插入1,it指向不变 /*it =*/ l.emplace(it, 2); // 哨兵节点前插入2,it指向不变 // 范围for遍历有效元素,从首节点到哨兵前截止,依次输出0、1、2 for (auto val : l) cout << val << endl; cout << "\n\n"; cout << *it << endl; // 解引用哨兵节点,未定义行为,输出3为内存垃圾值 return 0; }
内容的提问来源于stack exchange,提问作者ahmad_ahmad
相关产品推荐
相关产品推荐

