6.6.3 结点的删除

接下来看删除结点的函数__list_del(),其实现代码如下:


static inline void__list_del(struct list_headprev,struct list_headnext)

{

next->prev=prev;

prev->next=next;

}


对于prev和next指针所指向的结点,两者互相所指,也就是说,prev为待删除结点的前一个结点,next为待删除结点的后一个结点。在上面的代码中,仅仅将所要删除的结点从链表中“拿掉”了,并没有对其前指针域和后指针域进行相应的处理,这对于结点来说无疑是危险的,所以在list.h中通常会采用以下这个删除结点的函数。


static inline void list_del(struct list_head*entry)

{

__list_del(entry->prev,entry->next);

entry->next=LIST_POISON1;

entry->prev=LIST_POISON2;

}


上面的代码实现了删除entry所指的结点,同时将entry所指向的结点指针域“封死”。而实现“封死”操作的两个值分别是LIST_POISON1、LIST_POISON2。它们的值到底是什么呢?它们在list.h中是如下定义的:


define LIST_POISON1((void*)0x00100100)

define LIST_POISON2((void*)0x00200200)


对LIST_POISON1、LIST_POISON2的说明,在Linux内核中有这么一句话:“These are non-NULL pointers that will result in page faults under normal circumstances,used to verify that nobody uses non-initialized list entries.”,也就是说,这两者并不是空指针,但是访问这样的指针在正常情况下会出错。按照一般的思路,把entry->next和entry->prev赋值为NULL,不可以通过该结点对链表再次进行访问,但是在这里使用了一种特殊的方法。注意,在Linux环境下,以上宏LIST_POISON1和LIST_POISON2的值不进行修改是不会出错的,但是在VC下就会出错,要将((void)0x00100100)和((void)0x00200200)均修改为NULL。

如果要将删除的结点作为链表头另外创建一个链表,那么可以采用下面的函数来实现,代码如下:


static inline void list_del_init(struct list_head*entry)

{

__list_del(entry->prev,entry->next);

INIT_LIST_HEAD(entry);

}


以上函数的功能为删除entry所指向的结点,同时调用INIT_LIST_HEAD()把被删除结点作为链表头构建一个新的空双循环链表。

通过讲解了如何通过哪些函数来实现结点的删除操作,接下来通过代码来看看具体的实现方法。


include<stdio.h>

include<stdlib.h>

include"list.h"

typedef struct_stu

{

char name[20];

int num;

struct list_head list;

}stu;

int main()

{

stu*pstu;

stu*tmp_stu;

struct list_head stu_list;

struct list_head*pos;

int i=0;

INIT_LIST_HEAD(&stu_list);

pstu=malloc(sizeof(stu)*5);

for(i=0;i<5;i++)

{

sprintf(pstu[i].name,"Stu%d",i+1);

pstu[i].num=i+1;

list_add(&(pstu[i].list),&stu_list);

}

list_del(&(pstu[3].list));

printf("使用list_del()删除pstu[3]\n");

list_for_each(pos,&stu_list)

{

tmp_stu=list_entry(pos,stu,list);

printf("student num:%d\tstudent name:%s\n",tmp_stu->num,tmp_stu->name);

}

list_del_init(&(pstu[2].list));

printf("使用list_del_init()删除pstu[2]\n");

list_for_each(pos,&stu_list)

{

tmp_stu=list_entry(pos,stu,list);

printf("student num:%d\tstudent name:%s\n",tmp_stu->num,tmp_stu->name);

}

free(pstu);

return 0;

}


运行结果:


root@ubuntu:/home/paixu/dlist_node#./a

使用list_del()删除pstu[3]

student num:5 student name:Stu5

student num:3 student name:Stu3

student num:2 student name:Stu2

student num:1 student name:Stu1

使用list_del_init()删除pstu[2]

student num:5 student name:Stu5

student num:2 student name:Stu2

student num:1 student name:Stu1


在以上的代码中创建了5个结点,然后调用list_del()函数删除了pstu[3],调用list_del_init()函数删除了pstu[2],从打印结果可以看出成功地调用了这两个删除函数实现了链表中结点的删除操作。