关于在单向链表中删除一个指定的节点,通常有两个易错点。
//通常的写法
typedef struct node
{
struct node * next;
....
} node;
typedef bool (* remove_fn)(node const * v);
// Remove all nodes from the supplied list for which the
// supplied remove function returns true.
// Returns the new head of the list.
node * remove_if(node * head, remove_fn rm)
{
for (node * prev = NULL, * curr = head; curr != NULL; )
{
node * const next = curr->next;
if (rm(curr))
{
if (prev)
prev->next = next;
else
head = next;
free(curr);
}
else
prev = curr;
curr = next;
}
return head;
}
//Linus所说的利用二级指针的写法。
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next; //分离要删的节点
free(entry); //回收内存
}
else
curr = &entry->next; //->符号优先于&
}
}
第二种写法使用了一个二级指针指向那个指向node节点的指针。关于这个实现,那个文章里已经说的很清楚了。这里只是记录一下对于这个实现的看法。
二级指针的关键在于:二级指针让head指针和next指针在某个特点上处于同一水平。head指针和next最大的不同点是next指针能被别的指针操作。因为next指针属于node结构体中,所以指向node结构体的指针可以携带操作next指针。而head指针没有,没有指针指向它,要修改head的值或者读取head,需要在代码中写明head。而二级指针就解决这个问题,这也就体现了什么是指针。这就是为什么linus说用第一种写法的人不懂指针。因为指针的强大之处在于它能指向任何东西,包括另一个指针。让一个固定了变量名的变量(甚至是一个函数)能作为一个值一样被传递。
是不是必须用二级指针呢?我表示如果单纯为了去掉head指针这个麻烦,我最简单的想法是可以让head指针实体化,即让head先指向一个没用的节点,再让这个节点的next指向链表头节点。