linus大佬的代码见解。原文:https://github.com/mkirchner/linked-list-good-taste
以删除链表的某个节点为例子,链表数据结构如下:
struct list_item {
int value;
struct list_item *next;
};
typedef struct list_item list_item;
struct list {
struct list_item *head;
};
typedef struct list list;
删除的函数声明:
/* The textbook version */
void remove_cs101(list *l, list_item *target);
/* A more elegant solution */
void remove_elegant(list *l, list_item *target);
CS101实现:
void remove_cs101(list *l, list_item *target)
{
list_item *cur = l->head, *prev = NULL;
while (cur != target) {
prev = cur;
cur = cur->next;
}
if (prev)
prev->next = cur->next;
else
l->head = cur->next;
}
优雅的实现:
void remove_elegant(list *l, list_item *target)
{
list_item **p = &l->head;
while (*p != target)
p = &(*p)->next;
*p = target->next;
}
优雅的实现原理:
首先l->head是指向列表第一项的指针,初始化一个list_item **类型的变量p,取指向head指针的地址(&l->head),使其赋给p。也就是说p是指针的指针。
接下来通过循环找到目标指针:更新p来遍历list, *p表示将指向当前列表项的指针的地址解引用,*p->next表示下一个列表项的地址,&再取地址的地址赋给p。
最后将目标节点移除(通过赋值方式来覆盖目标节点)。
为什么使用间接指针?
使用pre和cur需要判断null的情况,但是用间接指针可以避免这种状况,同时用地址更新的方法遍历,只需要单个迭代器。
扩展
将优雅的实现包装成查找函数
static list_item** find_index(list* l, list_item* target)
{
list_item** p = &l->head;
while (*p != target)
{
p = &(*p)->next;
}
return p;
}
void remove_elegant(list* l, list_item* target)
{
list_item** p = find_index(l, target);
*p = target->next;
}
在某个节点前插入:
void insert(list* l, list_item* before, list_item* item)
{
list_item** p = find_index(l, before);
*p = item;
item->next = before;
}