vector<int*>指向vector<int>元素出现未定义行为的原因排查
vector<int*>指向vector元素时的指针失效问题
我尝试创建一个vector<int*>,让它的元素指向vector<int>的元素(用于竞赛编程场景),但运行代码时出现异常:第一行输出的指针指向值不符合预期,不同编译器输出结果不同,期望两行输出内容一致。
原代码
#include <bits/stdc++.h> using namespace std; int ct = 0; vector<int> vec; vector<int*> rf; void addRef(int n){ vec.push_back(n); rf.push_back(&vec[ct]); ct++; } int main(){ addRef(1); addRef(2); addRef(5); for(int i = 0; i < ct; i++){ cout << *rf[i] << ' '; } cout << endl; for(int i = 0; i < ct; i++){ cout << vec[i] << ' '; } }
运行结果
Dev-C++输出:
1579600 2 5 1 2 5
在线编译器输出:
1197743856 0 5 1 2 5
原因分析
核心问题是vector的自动扩容机制导致指针失效:
- vector初始化时容量很小,当调用
push_back且当前容量不足以容纳新元素时,会自动分配一块更大的内存空间,把原有元素全部拷贝到新内存,再释放旧内存。 - 你在
addRef中保存的是旧内存里元素的地址,旧内存被释放后,这些指针就变成了悬空指针,访问悬空指针属于C++未定义行为,所以不同编译器会输出不同的乱码值。
实现错误
在addRef函数中,每次vec.push_back(n)后直接取&vec[ct]的地址,但没考虑到push_back可能触发扩容,导致之前保存的指针指向的内存已被回收,后续访问必然出错。
修正方案
方案一:提前预留足够容量(最简单直接)
在main开头给vec预留足够容量,避免扩容操作,这样元素的地址就不会改变:
#include <bits/stdc++.h> using namespace std; int ct = 0; vector<int> vec; vector<int*> rf; void addRef(int n){ vec.push_back(n); rf.push_back(&vec[ct]); ct++; } int main(){ vec.reserve(3); // 提前预留至少3个元素的容量,按需调整 addRef(1); addRef(2); addRef(5); for(int i = 0; i < ct; i++){ cout << *rf[i] << ' '; } cout << endl; for(int i = 0; i < ct; i++){ cout << vec[i] << ' '; } }
方案二:用索引代替指针(竞赛推荐)
竞赛中更推荐直接保存元素的索引,vector的索引在不插入/删除中间元素的情况下是稳定的,完全可以替代指针,还能避免指针失效问题:
#include <bits/stdc++.h> using namespace std; int ct = 0; vector<int> vec; vector<int> rf; // 保存索引,而非指针 void addRef(int n){ vec.push_back(n); rf.push_back(ct); // 保存当前元素的索引 ct++; } int main(){ addRef(1); addRef(2); addRef(5); for(int i = 0; i < ct; i++){ cout << vec[rf[i]] << ' '; // 通过索引访问 } cout << endl; for(int i = 0; i < ct; i++){ cout << vec[i] << ' '; } }
方案三:使用不扩容的容器(按需选择)
如果一定要用指针,可以改用固定大小的array,或者用list(list的元素地址不会因插入改变,但随机访问效率低,竞赛中很少用)。
内容的提问来源于stack exchange,提问作者Sigmalalalala
相关产品推荐
相关产品推荐

