释放链表节点时出现分段错误

时间:2014-01-01 08:17:09

标签: c++ pointers null segmentation-fault free

LNode * deleteNext (LNode *L) {
  if (L == NULL) { return L; }

  LNode *deleted = L->next;
  L->next = L->next->next;
  //L->next->next = NULL;

  delete deleted;
  return L->next;
}

这是删除指向节点的下一个节点的函数,简单逻辑。目前的代码工作正常。但如果我取消注释评论行,就会出现分段错误,这对我来说似乎很奇怪。提前谢谢。

2 个答案:

答案 0 :(得分:1)

这是一个错误的实现。如果L->next为NULL,该怎么办?

这是一个可能的(正确的)实现:

LNode * deleteNext (LNode *L) 
{
  if (L == NULL || L->next == NULL) return NULL;

  LNode *deleted = L->next; //L->next is NOT NULL
  L->next = L->next->next;  
          //^^^^^^^^^^^^ could be NULL though

  delete deleted;
  return L->next; //L->next could be NULL here
}

现在由您决定从函数返回的内容取决于您。您可以返回L而不是L->next,或者您可以返回包含std::pair<LNode*, bool>的{​​{1}}和一个表示删除是否已完成的布尔值。

答案 1 :(得分:0)

这完全取决于你的列表的头部和尾部是如何实现的。我将假设列表的最后一个元素将其下一个链接设置为null(即列表本身不是一个闭环)。

电话在概念上是错误的。如果不保留对其头部(第一个元素)的引用,则无法处理单链接列表,除非您使用第一个元素作为头部,这是丑陋且低效的。

此外,您必须决定如何处理已删除的元素。删除它然后返回指向它仍然温暖的尸体的指针无论如何也不是最好的选择。

我将假设调用者可能对检索元素感兴趣(在这种情况下,调用者在完成使用后必须删除它)。

LNode * removeNext (LNode *L)
{
  if (L == NULL) panic ("Caller gave me a null pointer. What was he thinking?");
  // should panic if the caller passes anything but a valid element pointer,
  // be it NULL or 0x12345678

  LNode * removed = L->next;
  if (removed = NULL) return NULL; // L is the end of list: nothing to remove
  L->next = removed->next; // removed does exist, so its next field is valid

  // delete removed; // use this for the void deleteNext() variant
  return removed;
}

这将无法完全清空列表。至少有一个元素会留在其中(伪头,可以这么说)。

此外,您还必须使用上述伪头初始化列表。使用伪头调用removeNext是安全的,它相当于将列表用作LIFO。 尽管如此,这种实现方式不容易用作FIFO,因为没有简单的方法来维护对列表尾部(最后一个元素)的固定引用。

我这样做的方式就像这样:

typedef struct _buffer {
   struct _buffer * next;
   unsigned long    data;
   } tBuffer;

typedef struct {
   tBuffer * head;
   } tLIST; 

/* ---------------------------------------------------------------------
   Put a buffer into a list
   --------------------------------------------------------------------- */
static void list_put (tLIST * mbx, tBuffer * msg)
{
   msg->next = mbx->head;
   mbx->head = msg;
}

/* ---------------------------------------------------------------------
   get a buffer from a list
   --------------------------------------------------------------------- */
static tBuffer * list_get (tLIST * mbx)
{
   tBuffer * res;

   /* get first message from the mailbox */
   res = mbx->head;

   if (res != NULL)
      {
      /* unlink the buffer */
      mbx->head = res->next;
      }

   return res;
}

(我在90年代中期写过这篇文章。真正的老式ANSI C.啊,那些日子......)

归结为:如果你要实现一个单链表,不要试图像随机访问数据结构一样使用它。它充其量只是效率低下,而且往往是一堆bug。单链表可以用作FIFO或可能是堆栈,就是它。

std :: templates为您提供存储结构方面的所有梦想,并且在过去20年左右的时间里经过了测试和优化。没有人活着(可能除了Donald Knuth)从头开始设计可以做得更好。