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

Segmentation fault (core dumped) when I delete pointer

本文关键字:核心 转储 错误 分段 指针 删除      更新时间:2023-10-16

>我正在尝试从链表中删除重复项,并遇到了一个问题,这可能是显而易见和直接的,但是我已经很多年没有使用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的东西.很公平,但是在main中构建整个列表时,我正在new每个Node

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编辑!

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

编辑1:根据对我的帖子的回复使其更具描述性编辑2:添加了 3 个方法的规则

如果您分配了内存new,则无法使用free()

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

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

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

delete temp;

这是对其他答案的补充,可以正确识别和解决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他们接触的两个节点,并调用死亡风暴,这将摧毁列表中的所有节点。

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

很有可能Node析构函数应该照顾好自己,而将其他Node的销毁留给LinkedList类。

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

你可以delete new其他地方的东西。只要您指向有效的地址,这不是问题。然后,当您尝试使用指向现在delete的地址的指针之一时,会出现赛段错误。

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

您应该只在使用malloc时使用free,在使用newdelete。你永远不应该把它们混为一谈。另外,在delete之后,养成将NULL分配给指针变量的习惯。

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

封装数据的类也应该在内部执行自己的内存管理。例如,LinkedList应管理节点的内存。

这意味着,如果你有类似于LinkedList<T>::add(Node<T> *data)的东西,你应该考虑类似LinkedList<T>::add(T data)的东西;然后列表类本身负责处理节点。

否则,您将冒着将new ed节点发送到列表中的风险,然后列表在某个时候在内部delete它,但在其他地方,列表的客户端仍然持有对现在无效的节点的引用。当客户端尝试使用它时,砰!:再次出现段错误。

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

除了将 new s 与 free s 混合在一起之外,段错误很可能是由无效的内存访问触发的,因此您应该找到不再指向有效(即 un deleted(内存的指针。

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

默认情况下,您应该在构造函数中使用"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--;
    }
}

有很多答案表明new/deletemalloc()/free()应该总是配对,但我觉得应该说出确切的原因

就像您的初始化一样,

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

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

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