zoukankan      html  css  js  c++  java
  • 通过二级指针删除链表节点看链表本质

    在大多数情况下,会用到链表,但是一直都没有仔细深究过,今天抽点时间做了个实验。

    在看内核源码时,会发现使用二级指针插入或删除节点的情况,比如在字符设备里面的__register_chrdev_region及在notifier_chain_register里面。如下:

    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
            if ((*cp)->major > major ||
                ((*cp)->major == major &&
                 (((*cp)->baseminor >= baseminor) ||
                  ((*cp)->baseminor + (*cp)->minorct > baseminor))))
                break;
    
       ...
    
        cd->next = *cp;
        *cp = cd;

    当找到要插入的地方(*cp),其实cp是上个节点的next指针所存放的地址,修改cp其实是修改了上个节点的next指针的指向。将被插入元素的next指针指向*cp指向的位置(即下一个节点),然后将*cp指针改向,改指为cd(该句保证与之前的链表元素连接不间断)。
    其实精髓在于修改指针的指向,跟C里面的传地址和传值一样。

    自己写了一个程序来验证一下:

    #include <stdio.h>
    #include <stdlib.h>
    struct linklist
    {
        int num;
        struct linklist *next;
    };
    
    struct linklist head = {.num = 0, .next = NULL};
    
    int main()
    {
        int i;
        struct linklist *tmp = NULL;
        struct linklist **ttmp = NULL;
        for(i = 1; i < 6; i += 2)
        {
            tmp = (struct linklist *)malloc(sizeof(*tmp));
            tmp->num = i;
            tmp->next = head.next;
            head.next = tmp;
        }
    
        ttmp = &(head.next);
        while(*ttmp)
        {
             printf("%d, %016x, %016x, %016x
    ", (*ttmp)->num, ttmp, *ttmp, (*ttmp)->next);
             ttmp = &((*ttmp)->next);
        }
    
        printf("============================
    ");
        struct linklist addnode = {.num = 2, .next = NULL};
        ttmp = &(head.next);
        while(*ttmp)
        {
            if ((*ttmp)->num < addnode.num)
            {
                break;
            }
            ttmp = &((*ttmp)->next);
        }
        addnode.next = *ttmp;
        *ttmp = &addnode;
    
        ttmp = &(head.next);
        while(*ttmp)
        {
            printf("%d, %016x, %016x, %016x
    ", (*ttmp)->num, ttmp, *ttmp, (*ttmp)->next);
            ttmp = &((*ttmp)->next);
        }
    
    ...
    
    return 0;
    }

    运行结果如下:

    这种方法还适用于删除链表节点的场景,代码类似如下:

    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;
        }
    }

    这种方法避免了传统的删除或插入链表节点需要记录链表prev节点的步骤,如下:

    // 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;
    }
  • 相关阅读:
    python之set
    python之tuple
    python之list
    python之Number
    LAMP源码安装,搭建zabbix监控
    linux sshd服务
    linux rsync服务
    linux 实时同步inotify
    搭建LNMP;搭建WIKI
    数字,列表,函数
  • 原文地址:https://www.cnblogs.com/chaozhu/p/6510415.html
Copyright © 2011-2022 走看看