删除指针时出现分段错误(核心转储)

时间:2015-10-13 19:41:42

标签: c++ pointers linked-list segmentation-fault duplicate-removal

我正在尝试从链接列表中删除重复项,并遇到了一个问题,这可能很明显且很简单,但我多年没有使用过C++而我无法找到我在做什么在SO上阅读类似的问题是错误的。

以下是我的部分代码。我删除了不相关的部分(例如构造函数,其他方法等)。

template<class T>
class Node {
  Node() : data(NULL), next(NULL), prev(NULL) {}
  explicit Node(T d) : data(d), next(NULL), prev(NULL) {}
  explicit Node(T d, Node<T> *nxt, Node<T> *prv) : data(d), next(nxt),    prev(prv) {}
  ~Node() { delete next; delete prev; }

  T data;
  Node *next;
  Node *prev;
};

template<class T>
class LinkedList {
  LinkedList() : head(NULL) {}
  explicit LinkedList(Node<T> *head_node) : head(head_node) {}
  LinkedList& operator=(const LinkedList &copy_list);
  ~LinkedList(); //didn't implement this but I guess delete head is all?

  Node<T>* head;
};


template<class T>
LinkedList<T> RemoveDuplicates(const LinkedList<T> &linked_list) {
  //my = overload creates a whole new list with the same data as the original one
  LinkedList<T> result = linked_list; 

  Node<T> *cur = result.head;
  Node<T> *prev = NULL;

  while (cur) {
    if (...) { //duplicate found  
      Node<T> *temp = cur;
      prev->next = cur->next;
      cur = cur->next;
      cur->prev = prev;
      free(temp); //Here is my problem!!!
    }
    else {
      prev = cur;
      cur = cur->next;
    }
  }
  return result;
}

首先,我做了delete temp,我得到了Segmentation fault。然后我意识到你只deletenew。很公平,但我在new中构建整个列表时Node main Node<char> *h = new Node<char>('H'); //I have a constructor to handle this Node<char> *e = new Node<char>('E'); Node<char> *l1 = new Node<char>('L'); Node<char> *l2 = new Node<char>('L'); Node<char> *o = new Node<char>('O'); h->next = e; h->prev = NULL; e->next = l1; e->prev = h; //and so on

delete

那么为什么我不被允许new在其他地方new发送的内容?是因为它是free在当前范围之外吗?

其次,malloc空间工作正常,但显然不是正确的事情,因为我没有new而是$ sudo ntpdate ntp.org ed!

我做错了什么?如何正确杀死被删除的节点?

Edit1:根据我的帖子回复,使其更具描述性 Edit2:添加了3个方法的规则

5 个答案:

答案 0 :(得分:4)

如果您使用free()

分配内存,则无法使用new

您可以将malloc()free()newdelete一起使用。您也不应该将malloc()free()与对象一起使用,因为它们不会调用对象构造函数和析构函数,而不像newdelete

因此,由于您使用new分配了节点,我们可以更改

free(temp); //Here is my problem!!!

delete temp;

答案 1 :(得分:2)

在这种情况下,拥有构造函数和析构函数的代码非常重要。

默认情况下,你应该在构造函数中使用“new”,在析构函数中使用“delete”。更具体地说,在构造函数中分配所需的所有资源并在析构函数中释放是非常重要的,这样可以确保没有任何内存泄漏。

free(temp);//You don't need this, also you don't need delete.

请发布构造函数和析构函数代码。

L.E:

我认为你应该这样做:

template<class T>
class LinkedList
{
  private:
    struct Node {
        T m_data;
        Node *m_next;
        Node *m_prev;
    };
    Node* m_first;
    Node* m_last;
    int m_count;
  public:
    LinkedList();
    ~LinkedList();
    T GetFirst();
    T GetLast();
    void AddNode(T node);
    void RemoveAt(int index);
    T GetAt(int index);
    int Count();
};

//constructor
template <typename T>
LinkedList<T>::LinkedList(){
    m_count = -1;//-1 == "The list is empty!
}

template <typename T>
void LinkedList<T>::AddNode(T data){
    Node* temp = new Node; //new is called only here -> delete is called in destructor or in RemoveAt
    temp->m_data = data;
    if (m_count > -1){//If the list contains one or more items
        temp->m_next = m_first;
        temp->m_prev = m_last;
        m_last->m_next = temp;
        m_last = temp;
        m_count++;
    }
    else if (m_count == -1)
    {//If no items are present in the list
        m_first = temp;
        m_first->m_next = temp;
        m_first->m_prev = temp;
        m_last = temp;
        m_last->m_next = temp;
        m_last->m_prev = temp;
        m_count = 0;
    }
}

template <typename T>
void LinkedList<T>::RemoveAt(int index){
    if (index <= m_count)//verify this request is valid
    {
        Node* temp = m_last;
        for (int i = 0; i <= index; ++i){
            temp = temp->m_next;
        }
        temp->m_prev->m_next = temp->m_next;
        temp->m_next->m_prev = temp->m_prev;
        delete temp;
        m_count--;
    }
}

template <typename T>
T LinkedList<T>::GetAt(int index)
{
    Node* temp = m_first;
    if (index <= m_count && index > -1){
        int i = 0;
        while(i < index){
            temp = temp->m_next;
            i++;
        }
    }
    return temp->m_data;
}

template <typename T>
T LinkedList<T>::GetFirst() {
    return m_first->data;
}

template <typename T>
T LinkedList<T>::GetLast(){
    return m_last->data;
}

template <typename T>
int LinkedList<T>::Count(){
    return m_count;
}

template <typename T>
LinkedList<T>::~LinkedList(){
   while(m_count > -1){
        Node* temp = m_first;
        m_first = temp->m_next;
        delete temp;//delete in destructor
        m_count--;
    }
}

答案 2 :(得分:2)

  

那么为什么我不允许删除某处新出现的东西   其他?是因为它是在当前范围之外新建的吗?

您可以在其他位置delete new件事。只要您指向有效地址,这不是问题。当您尝试使用指向现在的delete ed地址的指针之一时,会出现seg-fault。

  

其次,释放空间效果很好,但我觉得这就像作弊一样!

使用freemalloc和使用deletenew时,您应该只使用delete。你应该从不混淆它们。此外,在NULL之后,养成将LinkedList分配给指针变量的习惯。

  

因为我可能需要在Node的析构函数上完成一些事情。

封装数据的类也应该在内部进行自己的内存管理。例如,LinkedList<T>::add(Node<T> *data)应该管理节点的内存。

这意味着,如果您有类似于LinkedList<T>::add(T data)的内容,则应考虑使用类似new的内容;然后列表类本身负责处理节点。

否则,您冒着将delete ed节点发送到列表中的风险,然后列表new在内部某处,但在其他地方,列表的客户端仍然保留对现在无效的节点。当客户端尝试使用它时,bam!:seg-fault再次出现。

  

我做错了什么?如何正确杀死被删除的节点?

除了将freedeleted混合使用之外,段错误很可能是由无效的内存访问触发的,因此您应该找到不再指向有效的指针(即un {{ 1}})记忆。

答案 3 :(得分:2)

这是对其他答案的补充,正确识别和解决OP的直接问题。需要快速步行来解释接下来会发生什么。

  Node<T> *temp = cur;
  prev->next = cur->next;
  cur = cur->next;
  cur->prev = prev;

此时temp尚未被清除,所以next和prev仍然指向用于包围cur的两个节点,现在它们已链接在一起。如果不是这样,这将导致严重的问题:

  free(temp); //Here is my problem!!!

free失败,因为temp指向的值是由new分配的。 NathanOliver的答案涵盖了这一点,但潜伏在下面的是

会发生什么
delete temp;

这会调用析构函数来清理一个好的小对象。不幸的是,Node析构函数看起来像这样:

~Node() { delete next; delete prev; }

temp->next仍然指向一个活动节点。 temp->prev也是如此。

接下来,上一步获得delete d。它调用了它们的析构函数,delete它们触摸的两个节点,并调用一个会破坏列表中所有节点的死亡风暴。

如果双重删除不首先杀死该程序。每个链接的节点将尝试deletedeleted它们的节点。不好。

Node析构函数应该只关注自己,并将其他Node的破坏留给LinkedList类。

答案 4 :(得分:1)

有许多答案表明new/deletemalloc()/free()应始终配对,但我觉得应该说为什么

与初始化一样,

Node<char> *h = new Node<char>('H');

new关键字返回一个完全类型化的对象对象指针,在语句本身中定义,而malloc()只是在不指定类型的情况下分配给定的内存空间,因此返回void* 。此外,new/delete处理来自Free Store的内存,而malloc()/free()在Heap上分配/释放内存,尽管某些编译器在某些情况下可能会new/free实现malloc()/free

同样重要的是newdelete分别调用构造函数和析构函数,而malloc()free()只是分配和释放堆内存,而不考虑记忆本身的状态。