修改C++ vector返回引用后Valgrind报错及关联崩溃分析
问题背景
尝试通过引用修改std::vector,期望在引用生命周期内,通过引用修改始终有效(即引用指向的对象不会被释放),但出现了内存错误。
最小可复现代码
#include <vector> class B { public: B() { m_b = 1; } int &getInt() { return m_b; } private: int m_b; }; class A { public: B &getB() { m_vec.push_back(B()); return m_vec.back(); } std::vector<B> getVec() { return m_vec; } private: std::vector<B> m_vec; }; int main() { A a; B &b1 = a.getB(); int i = b1.getInt(); B &b2 = a.getB(); b1 = B(); return 0; }
Valgrind检测输出
==22925== Memcheck, a memory error detector ==22925== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==22925== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==22925== Command: ./test ==22925== ==22925== Invalid write of size 4 ==22925== at 0x1092B5: main (test.cpp:27) ==22925== Address 0x4deec80 is 0 bytes inside a block of size 4 free'd ==22925== by 0x484BB6F: operator delete(void*, unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==22925== by 0x109E28: __gnu_cxx::new_allocator<B>::deallocate(B*, unsigned long) (new_allocator.h:145) ==22925== by 0x109B6F: std::allocator_traits<std::allocator<B> >::deallocate(std::allocator<B>&, B*, unsigned long) (alloc_traits.h:496) ==22925== by 0x1098D9: std::_Vector_base<B, std::allocator<B> >::_M_deallocate(B*, unsigned long) (stl_vector.h:354) ==22925== by 0x109A97: void std::vector<B, std::allocator<B> >::_M_realloc_insert<B>(__gnu_cxx::__normal_iterator<B*, std::vector<B, std::allocator<B> > >, B&&) (vector.tcc:500) ==22925== by 0x1096E5: B& std::vector<B, std::allocator<B> >::emplace_back<B>(B&&) (vector.tcc:121) ==22925== by 0x1094ED: std::vector<B, std::allocator<B> >::push_back(B&&) (stl_vector.h:1204) ==22925== by 0x1093F4: A::getB() (test.cpp:14) ==22925== by 0x10929D: main (test.cpp:26) ==22925== Block was alloc'd at ==22925== at 0x4849013: operator new(unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==22925== by 0x109FED: __gnu_cxx::new_allocator<B>::allocate(unsigned long, void const*) (new_allocator.h:127) ==22925== by 0x109EAC: std::allocator_traits<std::allocator<B> >::allocate(std::allocator<B>&, unsigned long) (alloc_traits.h:464) ==22925== by 0x109D65: std::_Vector_base<B, std::allocator<B> >::_M_allocate(unsigned long) (stl_vector.h:346) ==22925== by 0x1099C0: void std::vector<B, std::allocator<B> >::_M_realloc_insert<B>(__gnu_cxx::__normal_iterator<B*, std::vector<B, std::allocator<B> > >, B&&) (vector.tcc:440) ==22925== by 0x1096E5: B& std::vector<B, std::allocator<B> >::emplace_back<B>(B&&) (vector.tcc:121) ==22925== by 0x1094ED: std::vector<B, std::allocator<B> >::push_back(B&&) (stl_vector.h:1204) ==22925== by 0x1093F4: A::getB() (test.cpp:14) ==22925== by 0x10927C: main (test.cpp:24) ==22925== ==22925== ==22925== HEAP SUMMARY: ==22925== in use at exit: 0 bytes in 0 blocks ==22925== total heap usage: 3 allocs, 3 frees, 72,716 bytes allocated ==22925== ==22925== All heap blocks were freed -- no leaks are possible ==22925== ==22925== For lists of detected and suppressed errors, rerun with: -s ==22925== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
疑问
- 为何Valgrind会出现该无效写入错误?
- 原程序运行数毫秒后崩溃并打印
malloc(): unsorted double linked list corrupted,该崩溃是否与该内存bug相关?
问题解答
1. Valgrind无效写入错误的原因
std::vector底层依赖连续内存缓冲区。当调用push_back时,如果当前缓冲区剩余空间不足以容纳新元素,vector会重新分配一块更大的内存,将旧缓冲区的元素拷贝/移动到新内存后释放旧缓冲区。
在你的代码流程里:
- 第一次调用
a.getB(),vector为空,分配内存插入第一个B对象,返回该对象的引用b1。 - 第二次调用
a.getB()时,vector初始容量通常为1,已满触发重新分配:旧内存被释放,新内存分配并存放两个B对象。此时b1指向的是已经被释放的旧内存地址。 - 最后执行
b1 = B();,本质是往已释放的内存地址写入数据,这就是Valgrind检测到的无效写入。
2. 崩溃与内存bug的关系
是的,崩溃完全由该内存bug导致。
往已释放的内存写入数据会破坏malloc维护的堆内存管理结构(比如用于管理空闲块的双链表节点信息)。后续malloc/free操作尝试访问这些被破坏的结构时,就会触发malloc(): unsorted double linked list corrupted这类错误,直接导致程序崩溃。这种堆内存损坏的表现可能延迟出现,但根源就是之前的无效写入操作。
内容的提问来源于stack exchange,提问作者user1006274
相关产品推荐
相关产品推荐

