C++ 释放链表节点时出现分段错误
这是一个删除下一个点节点的函数,逻辑简单。当前代码运行良好。但是如果我取消注释注释行,就会出现分段错误,这在我看来很奇怪。提前感谢。这是一个错误的实现。如果C++ 释放链表节点时出现分段错误,c++,pointers,null,segmentation-fault,free,C++,Pointers,Null,Segmentation Fault,Free,这是一个删除下一个点节点的函数,逻辑简单。当前代码运行良好。但是如果我取消注释注释行,就会出现分段错误,这在我看来很奇怪。提前感谢。这是一个错误的实现。如果L->next为空怎么办 以下是一种可能的(正确的)实现: LNode * deleteNext (LNode *L) { if (L == NULL) { return L; } LNode *deleted = L->next; L->next = L->next->next; //L->n
L->next
为空怎么办
以下是一种可能的(正确的)实现:
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;
}
现在由您决定您希望从函数返回什么。您可以返回
L
而不是L->next
,也可以返回包含L
的std::pair
和一个指示是否完成删除的布尔值。这都取决于列表的头和尾是如何实现的。我将假设列表的最后一个元素的下一个链接设置为null(即列表本身不是一个闭合环)
这一呼吁在概念上是错误的。除非将第一个元素用作标题,否则在处理单个链接列表时,必须保留对其标题(第一个元素)的引用,这既丑陋又低效
您还必须决定如何处理删除的元素。删除它,然后返回一个指向它仍然温暖的尸体的指针,无论如何都不是最好的选择
我将假设调用方在检索元素时可能受到干扰(在这种情况下,调用方必须在使用完元素后将其删除)
这将无法完全清空列表。至少有一个元素会留在其中(可以说是伪头)
您还必须使用所述伪头初始化列表。使用伪头调用removeNext是安全的,它将等同于使用列表作为后进先出。
不过,这种实现不允许作为FIFO轻松使用,因为没有简单的方法来维护对列表尾部(最后一个元素)的固定引用
我会这样做:
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;
}
(我早在90年代中期就写了这篇文章。真正的复古ANSI C。啊,那些日子…)
归结起来就是:如果你要实现一个单链表,不要试图把它当作一个随机访问的数据结构来使用。它充其量是低效的,而且常常是一窝臭虫。单个链表可以用作FIFO,也可以用作堆栈,仅此而已
std::templates为您提供了存储结构方面的一切梦想,在过去20年左右的时间里,它们经过了测试和优化。没有一个活着的人(可能除了唐纳德·克努斯)能用从头开始的设计做得更好。如何调用该函数?(作为L传递什么?)你检查L是否为空,但如果为空,你不检查L->next或L->next->next。@Vladp很高兴你问,PRAM可能是链接列表中除尾部节点以外的所有节点。如果
L->next->next
NULL怎么办?@FredrickGauss:在这种情况下,函数返回NULL
(即L->next
在return
处将NULL
)。谢谢两位。很抱歉没有提到输入。实际上输入可能总是正确的,我的意思是,没有L->next为空的情况。L->next->next可以为空,除非它是尾部节点,其下一个将始终为空,所以它是安全的。@Briyanyang:错误的输入仍然是函数的责任。因此函数必须检查L->next==NULL
@Nawaz谢谢,明白了。根据我对这个函数的逻辑,它应该不会错。我的意思是,对于那个注释行。非常有趣。尤达说。请原谅我的法语,年轻学徒。英语我的母语不是;)。
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;
}
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;
}