std::vector的reserve、resize与堆内存破坏

本文讲述了在调试一个内存崩溃问题时,发现问题源自于std::vector使用reserve而非resize。当向vector中插入std::shared_ptr对象时,未初始化的内存会被析构,导致野指针删除,引发堆内存破坏。解决方案是使用resize进行默认初始化,或使用push_back。错误理解《Effective C++》中关于reserve的建议,是导致问题的原因之一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天费了一个下午调试一个诡异的内存崩溃问题(coredump)

基本上可以稳定复现,但会有不同。经分析崩溃栈,发现都是malloc内存时出现了signal 11段错误?

分配内存时崩溃有可能是传入了非法size值,比如说-1或者0什么的,但那种情况下会抛bad alloc异常,或者返回空指针。

起初怀疑是std::string的默认allocator的问题,甚至怀疑是全局变量未初始化导致的问题。。。

但是都不在点子上!

之后确定是堆内存破坏的情况下,祭起“注代码”大法!发现跟Node/v8的HandleScope、Local/Persistent这些没有什么关系。突然眼前一亮,如下的代码模式: 

std::vector<std::shared_ptr<T>> V;
V.reserve(N);
V[i] = std::make_shared<T>(...);

x见鬼,大概知道问题出在什么地方了:reserve仅仅分配STL容器所占的空间,但不会为每个元素执行空初始化。之后的V[i]=赋值操作实际上有一个副作用:会对V[i]的老元素执行一个析构操作。

问题是实际的元素类型是std::shared_ptr<T>!换句话说,会执行std::shared_ptr::~shared_ptr析构操作,进而执行delete T操作!考虑到V[i]的内存由于之前的reserve操作,实际上引用的是一块未初始化的内存,所以这潜在地导致对于一个野指针的delete操作,从而导致堆内存破坏。

修改方法:改reserve为resize,resize会执行默认初始化。或者把后面的V[i]=...改为V.push_back(...)。

我之所以写reserve而不是resize,正是受到之前看《Effective C++》一书的错误印象,那里面说到reserve预分配内存可以避免后续push_back操作潜在的容器resize开销,但问题是,我后面用的是V[i]=...,而不是push_back。

如果元素类型是T*,而不是std::shared_ptr<T>的话,也不会有什么问题。因为T*指针元素类型没有析构操作,属于primitive value类型。

之所以没有早些发现问题所在,原因是代码调试直接在嵌入式实机环境上的,如果是Windows桌面调试环境,这个问题要么要就被MSVC的调试器在debug模式下定位出来了,要么也能够排除其他的原因(比如说我会怀疑会不会是嵌入式ARM机器的不对齐内存访问导致的崩溃,代码里使用了enum class E : uint8_t {})很快定位到了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值