STL类跳跃列表实现的段错误

Seg fault of STL like skiplist implementation

本文关键字:错误 段错误 跳跃 列表 实现 STL      更新时间:2023-10-16

我正在实现stl风格的跳跃列表。内部节点类型如下:

template <class N>
struct __skiplist_node
{
  typedef __skiplist_node<N>* __skiplist_node_pointer;
  N data;
  __skiplist_node_pointer prev;
  std::list<__skiplist_node_pointer> nexts;
};

内部节点存在std::list。当我想push_back一个__skiplist_node到列表下一步。在运行时构造跳过列表时。遇到分段故障。调试背景显示:

#0  0x00007ffff63a6acf in std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x000000000045e0f6 in _M_insert (this=<optimized out>, __x=<optimized out>, 
    __position=...) at /usr/include/c++/4.8/bits/stl_list.h:1554
#2  push_back (__x=<optimized out>, this=<optimized out>)
    at /usr/include/c++/4.8/bits/stl_list.h:1016
#3  repo::SkipList<ndn::Name, repo::tests::DatasetBase::DataSetNameCompare>::empty_initialize (this=<optimized out>)
    at /home/chenatu/workspace/repo-ng/src/storage/skiplist.hpp:118
#4  0x000000000045ed1b in SkipList (this=0x7fffffffcfd0)
    at /home/chenatu/workspace/repo-ng/src/storage/skiplist.hpp:124
#5  repo::tests::SkipList::NdnNameSkipList<repo::tests::BaseTestFixture>::test_method (this=this@entry=0x7fffffffd070) at ../tests/unit/skiplist.cpp:40
#6  0x000000000045efd9 in run<repo::tests::BaseTestFixture> ()
    at ../tests/unit/skiplist.cpp:38
#7  operator() (this=<optimized out>)
    at /usr/include/boost/test/unit_test_suite_impl.hpp:357
#8  invoke<boost::unit_test::ut_detail::test_case_template_invoker<repo::tests::SkipList::NdnNameSkipList_invoker, repo::tests::BaseTestFixture> > (
    this=<optimized out>, f=...)
    at /usr/include/boost/test/utils/callback.hpp:56

我想知道的是如何动态分配这个内部节点?

下面是完整的代码:

#ifndef REPO_STORAGE_SKIPLIST_HPP
#define REPO_STORAGE_SKIPLIST_HPP
#include "common.hpp"
#include <boost/utility.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
namespace repo {
static const size_t SKIPLIST_MAX_LAYERS = 32;
static const double SKIPLIST_PROBABILITY = 0.25; // 25% (p = 1/4)
template<typename T, typename Compare = std::less<T> >
class SkipList
{
public:
  template <class N>
  struct __skiplist_node
  {
    typedef __skiplist_node<N>* __skiplist_node_pointer;
    N data;
    __skiplist_node_pointer prev;
    std::list<__skiplist_node_pointer> nexts;
  };
  class iterator : public std::iterator<std::bidirectional_iterator_tag, T>
  {
  public:
    typedef __skiplist_node<T>* link_type;
    link_type node;
    // constructor
    iterator(link_type x) : node(x) {}
    iterator() {}
    iterator(const iterator& x) : node(x.node) {}
    bool operator == (const iterator& x) const { return node == x.node; }
    bool operator != (const iterator& x) const { return node != x.node; }
    typename std::iterator<std::bidirectional_iterator_tag, T>::reference operator * () const
      { return (*node).data; }
    typename std::iterator<std::bidirectional_iterator_tag, T>::pointer operator -> () const
      { return &(operator*()); }
    iterator& operator ++ () {
      node = (link_type)(*((*node).nexts.begin()));
      return *this;
    }
    iterator& operator -- () {
      node = (link_type)((*node).prev);
      return *this;
    }
  };
protected:
  typedef __skiplist_node<T> skiplist_node;
public:
  typedef skiplist_node* link_type;
protected:
  link_type node;
  std::allocator<skiplist_node> skiplist_allocator;
  link_type
  get_node()
  {
    return skiplist_allocator.allocate(sizeof(skiplist_node));
  }
  void
  put_node(link_type p)
  {
    skiplist_allocator.deallocate(p);
  }
  link_type
  create_node(const T& x)
  {
    link_type p = get_node();
    construct(&p->data, x);
    return p;
  }
  void
  destroy_node(link_type p)
  {
    destroy(&p->data);
    put_node(p);
  }
  void
  empty_initialize()
  {
    node = get_node();
    node->prev = node;
    node->nexts.push_back(node);
  }
public:
  explicit
  SkipList()
  {
    empty_initialize();
  }
  ~SkipList() {}
public:
  iterator begin() const { return (link_type)(*(*node).nexts.begin()); }
  iterator end() const { return node; }
  bool empty() const { return *(node->nexts.begin()) == node; }
  size_t size() const
  {
    size_t result = 0;
    result = std::distance(begin(), end());
    return result;
  }
};
} // namespace repo
#endif // REPO_STORAGE_SKIPLIST_HPP

错误在这里:

  link_type
  get_node()
  {
    return skiplist_allocator.allocate(sizeof(skiplist_node));
  }
  link_type
  create_node(const T& x)
  {
    link_type p = get_node();
    construct(&p->data, x);
    return p;
  }

allocate 只是分配内存,它没有初始化。您必须调用construct来初始化它,但只针对data而不是节点的其他部分。

因此,您从未构建std::list,只是为它分配内存,因此在尝试使用此未初始化内存时遇到未定义行为。