代码之家  ›  专栏  ›  技术社区  ›  Ilya

递归结构的向量存在内存问题

  •  0
  • Ilya  · 技术社区  · 6 年前

    我完全被一段简单的代码卡住了,这段代码在内存中表现不好(正如Valgrind报道的那样)。我将其简化为这个简短的测试用例:

    #include <vector>
    
    struct el
    {
        el * next = nullptr;
    };
    
    class list
    {
    public:
        list(): tail(nullptr) {}
    
        void push_back()
        {
            el nw;
            m_list.push_back(nw);
    
            if (tail == nullptr)
                tail = &m_list.back();
            else
            {
                tail->next = &m_list.back();
                tail = tail->next;
            }
        }
    
    private:
        std::vector<el> m_list;
        el * tail;
    };
    
    int main()
    {
        list a;
        a.push_back();
        a.push_back();
        return 0;
    }
    

    我希望它创建一个包含2个结构的数组,其中第一个结构有一个指向第二个结构的指针。真正的数据源在销毁时会出现segfault崩溃,因此我认为这份报告很重要:

    ==1630== Invalid write of size 8
    ==1630==    at 0x400A37: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400969: main (in /home/ilya/Projects/algos/a.out)
    ==1630==  Address 0x5a86c80 is 0 bytes inside a block of size 8 free'd
    ==1630==    at 0x4C2A8DC: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==1630==    by 0x4011D5: __gnu_cxx::new_allocator<el>::deallocate(el*, unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400F79: std::_Vector_base<el, std::allocator<el> >::_M_deallocate(el*, unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400DA3: void std::vector<el, std::allocator<el> >::_M_emplace_back_aux<el const&>(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400B42: std::vector<el, std::allocator<el> >::push_back(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4009FF: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400969: main (in /home/ilya/Projects/algos/a.out)
    ==1630==  Block was alloc'd at
    ==1630==    at 0x4C29780: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==1630==    by 0x4012BB: __gnu_cxx::new_allocator<el>::allocate(unsigned long, void const*) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4010D2: std::_Vector_base<el, std::allocator<el> >::_M_allocate(unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400CC1: void std::vector<el, std::allocator<el> >::_M_emplace_back_aux<el const&>(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400B42: std::vector<el, std::allocator<el> >::push_back(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4009FF: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x40095D: main (in /home/ilya/Projects/algos/a.out)
    ==1630==
    ==1630== Invalid read of size 8
    ==1630==    at 0x400A42: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400969: main (in /home/ilya/Projects/algos/a.out)
    ==1630==  Address 0x5a86c80 is 0 bytes inside a block of size 8 free'd
    ==1630==    at 0x4C2A8DC: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==1630==    by 0x4011D5: __gnu_cxx::new_allocator<el>::deallocate(el*, unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400F79: std::_Vector_base<el, std::allocator<el> >::_M_deallocate(el*, unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400DA3: void std::vector<el, std::allocator<el> >::_M_emplace_back_aux<el const&>(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400B42: std::vector<el, std::allocator<el> >::push_back(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4009FF: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400969: main (in /home/ilya/Projects/algos/a.out)
    ==1630==  Block was alloc'd at
    ==1630==    at 0x4C29780: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==1630==    by 0x4012BB: __gnu_cxx::new_allocator<el>::allocate(unsigned long, void const*) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4010D2: std::_Vector_base<el, std::allocator<el> >::_M_allocate(unsigned long) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400CC1: void std::vector<el, std::allocator<el> >::_M_emplace_back_aux<el const&>(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x400B42: std::vector<el, std::allocator<el> >::push_back(el const&) (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x4009FF: list::push_back() (in /home/ilya/Projects/algos/a.out)
    ==1630==    by 0x40095D: main (in /home/ilya/Projects/algos/a.out)
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   user7860670    6 年前

    一旦您将_推回向量或以其他方式更改它,所有指向存储项的引用和指针都将无效,因此所有 tail next 字段将包含悬空指针。取消引用它们将导致未定义的行为。你实际上可以使用 std::list 或者(如果您只是为了学习而编写一些列表实现),您可以首先填充向量,然后收集指向存储项的指针,知道它们将保持有效。