如果要删除单向链表会如何做呢,很多人可能会使用一个Pre指针(很多教科书上也是如此实现的),如下代码
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;
}
这里remove_fn由调用查提供的一个是否删除当前实体结点的函数指针,其会判断删除条件是否成立。这段代码维护了两个节点指针prev和curr,标准的教科书写法——删除当前结点时,需要一个previous的指针,并且还要这里还需要做一个边界条件的判断——curr是否为链表头。
于是,要删除一个节点(不是表头),只要将前一个节点的next指向当前节点的next指向的对象,即下一个节点(即:prev->next = curr->next),然后释放当前节点。但是这样代码些是没能使用好指针的结果。下面代码演示了如何秒用二级指针对单链表进行删除
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;
}
}
可以先理解下 下面这段代码:
#include <iostream>
using namespace std;
int main()
{
list_t lA, lB, lC, lD;
list_t ** phead = &lA.next;
lA.next = &lB;
lA.a = 11;
lA.b = 12;
lB.next = &lC;
lB.a = 21;
lB.b = 22;
lC.next = &lD;
lC.a = 31;
lC.b = 32;
lD.next = NULL;
lD.a = 41;
lD.b = 42;
printf("%d\n", ((list_t *)phead)->a);
printf("%d\n", (*(list_t**)phead)->a);
printf("%d\n", (**(list_t***)phead)->a);
printf("%d\n", (***(list_t****)phead)->a);
printf("%d\n", ((list_t *)phead)->b);
printf("%d\n", (**(list_t**)phead).b);
printf("%d\n", (***(list_t***)phead).b);
printf("%d\n", (****(list_t****)phead).b);
system("pause");
return 0;
}
这里能这么使用 主要是Sreuct 的地址和指向下一个Struct的地址是一样的,也就是说指向下一个Struct的指针必须是放在开头 不然这么使用时会有问题的。
换言之,如果一个单向链表,next是第一个字段,我们可以用一个二级指针dummy引用一条链表上的所有节点。
struct node **dummy = &head->next;
一次解引用*dummy指向头结点head
二次解引用**dummy指向head下一个节点
三次解引用*(*(*dummy))指向第三个节点
以此类推……
如果我们需要找到从head开始的第N个节点,那么对dummy进行N次解引用即可,当然现实工程中一般不会用到这么tricky的语法糖,但使用一个变量同时引用相邻三个节点是很有用的;-)。