C++ 双重释放或损坏(出)
Posted
技术标签:
【中文标题】C++ 双重释放或损坏(出)【英文标题】:C++ double free or corruption(out) 【发布时间】:2020-02-16 13:29:25 【问题描述】:我在使用带有向量 STL 的迭代器时遇到了这个错误。
代码:-
#include <iostream>
#include <vector>
void print_vec(std::vector<int> vec)
auto itr = vec.begin();
while (itr != vec.end())
std::cout << *itr++ << " ";
std::cout << std::endl;
int main(int argc, char* argv[])
std::vector<int> vals = 1, 2, 3, 4, 5, 6 ;
print_vec(vals);
auto it = vals.begin();
vals.insert(it, 100);
print_vec(vals);
it += 4;
std::cout << *it << "\n";
vals.insert(it, 200);
print_vec(vals);
return 0;
输出:-
1 2 3 4 5 6
100 1 2 3 4 5 6
5
0 100 1 2 3 4 5 6
double free or corruption (out)
Aborted (core dumped)
我不明白为什么会这样:修改向量后迭代器有问题吗?我认为我对迭代器的直觉是有缺陷的。
【问题讨论】:
std::vector<int>::iterator itr=vec.begin();
--> auto itr = vector.begin();
,拜托。为了可读性。
代码正在与迭代器失效赌博。
【参考方案1】:
当调整std::vector
的大小时,迭代器变得无效,因为它引用了调整大小之前 的向量,这可能包括将向量按顺序移动 到不同的内存位置保持连续性。在这种情况下,it
不会自动更新以引用新位置。
结果在这种情况下(因为它是未定义行为)是值200
没有插入向量中,但它被写入了其他一些向量先前占用的堆内存已被释放 - 即您损坏了堆。退出main时vector被销毁,此时系统堆管理失败并报错(事后)。
在插入之后重新分配迭代器可以解决问题:
it = vals.insert(it,100);
...
如您所料,单个项目的输出报告为 5 而不是 4,因为它只是读取尚未重用的旧向量内存并被其他内容覆盖。
您可以通过比较观察错误:
vals.insert(it,100);
std::cout<<std::distance(it,vals.begin())<<'\n' ;
与
it = vals.insert(it,100);
std::cout<<std::distance(it,vals.begin())<<'\n' ;
您的结果可能会有所不同,但在https://www.onlinegdb.com/,当我执行测试时,第一种情况下迭代器位置和向量开始之间的距离为 16 - 表明迭代器不再引用当前的元素向量状态。在第二种情况下,距离为零 - 迭代器指的是新向量状态的开始。
如果vals
的容量被扩展之前 以迭代器为例:
vals.reserve(10);
然后单个元素的插入不会导致内存移动,并且不会发生失败-尽管不更新迭代器并不是一个好主意-它只是表明发生了什么引擎盖。
【讨论】:
【参考方案2】:...修改向量后迭代器有问题吗?
是的!
来自cppreference page 上的std::vector.insert()
(我的粗体字):
如果新的 size() 大于旧的,则导致重新分配 容量()。如果新的 size() 大于 capacity(),all 迭代器和引用无效。否则,只有 插入点之前的迭代器和引用仍然有效。这 过去的迭代器也失效了。
【讨论】:
it = vals.insert(it,100); // 修复它以上是关于C++ 双重释放或损坏(出)的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 重启使用 MySQL C API 导致双重释放或损坏
使用 memset 和 freeaddrinfo 会导致双重释放或损坏错误