• 内核双链表篇:list.h——list_for_each宏与list_for_each_safe宏(遍历链表)


    说明
      本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
      QQ 群 号:【相互学习】
    内容来源
      

    一、list_for_each宏

    1.功能简介:

      遍历双链表,不适合与遍历中进行删除节点的操作。

    2.源码及注释:

     /* 
          功能:遍历head指向的头链表
          参数:
              p:指向下一个节点的链表指针 
              head:头指针
      */
    #define list_for_each(p, head) \
        for ((p) = (head)->next; (p) != (head); (p) = (p)->next)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.源文件测试理解步骤

    main.c
    #include 
    #include 
    #include "list.h"
    
    /* 链表节点 */
    typedef struct _TestStruct
    {
        struct list_head list;
        int num;
        bool pending;
        char name;
        void *pointer;
    }struct_node;
    
    static struct list_head dlist = LIST_HEAD_INIT(dlist);  /* 初始化链表: */
    
    int main(int argc, char *argv[])
    {
        struct_node *tmp = NULL;                            /* 暂存链表节点结构体 */
        struct list_head *head = &dlist;                    /* 链表表头 */
        struct list_head *p = NULL;
        int i = 0;
    
        //插入5个数据,使用头插法,类似于栈
        for (i = 0; i < 5; i++)
        {
            tmp = (struct_node *)malloc(sizeof(struct_node));
            tmp->num = i;
            list_add_tail(&tmp->list, head);                /* 插入链表尾部 */
        }
    
        /* 
            功能:遍历head指向的头链表
            参数:
                p:指向下一个节点的链表指针 
                head:头指针
        */
        list_for_each(p, head)
        {
            tmp = container_of(p, struct_node, list);
            printf("%d \n", tmp->num);
        }
    
        printf("------------------------------------\n");
    
        for ((p) = (head)->next; (p) != (head); (p) = (p)->next)
        {
            tmp = container_of(p, struct_node, list);
            printf("%d \n", tmp->num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    Makefile
    TARGET:=test
    SRCS:=main.c
    OBJS:=$(SRCS:.c=.o)#变量替换函数,将所有*.c文件替换为*.o文件,与OBJS=$(patsubst %.c,%.o,$(SRCS))等效
    
    CC:=gcc#编译器
    CFLAGS:=-Wall -std=gnu99#gcc选项
    
    $(TARGET):$(OBJS)
    	$(CC) -o $@ $^
    
    clean:
    	rm -rf $(TARGET) $(OBJS)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    结果
    xsndz@Linux:~/Desktop/list$ ./hello 
    0 
    1 
    2 
    3 
    4 
    ------------------------------------
    0 
    1 
    2 
    3 
    4 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二、list_for_each_safe宏

    1.功能简介:

      遍历链表,相较于list_for_each多增加了一个中间参数,保存当前结点的下个结点,避免当前结点删除后找不到下个结点。

    2.源码及注释:

    /*
          功能:遍历链表(可用于删除结点)
          参数:
              p :指向下一个节点的链表指针
              n :指向p的下一个节点的链表指针
              head:头指针
      */
    #define list_for_each_safe(p, n, head)                     \
        for ((p) = (head)->next, n = (p)->next; (p) != (head); \
             (p) = n, n = (p)->next)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.源文件测试理解步骤

    main.c
    #include 
    #include 
    #include "list.h"
    #include "dlist_test.h"
    
    /* 链表节点 */
    typedef struct _TestStruct
    {
        struct list_head list;
        int num;
        bool pending;
        char name;
        void *pointer;
    }struct_node;
    
    static struct list_head dlist = LIST_HEAD_INIT(dlist);  /* 初始化链表: */
    
    int main(int argc, char *argv[])
    {
        struct_node *tmp = NULL;                            /* 暂存链表节点结构体 */
        struct list_head *head = &dlist;                    /* 链表表头 */
        struct list_head *p = NULL, *n = NULL;
        int i = 0;
    
        //插入5个数据,使用头插法,类似于栈
        for (i = 0; i < 5; i++)
        {
            tmp = (struct_node *)malloc(sizeof(struct_node));
            tmp->num = i;
            list_add_tail(&tmp->list, head);                /* 插入链表尾部 */
        }
    
        /*
            功能:遍历链表(可用于删除结点)
            参数:
                p :指向下一个节点的链表指针
                n :指向p的下一个节点的链表指针
                head:头指针
        */
        // for ((p) = (head)->next, n = (p)->next; (p) != (head); (p) = n, n = (p)->next)
        list_for_each_safe(p,n,head)
        {
            tmp = list_entry(p, struct_node, list);        /* 等价于container_of */
    
            if (tmp->num == 1)
            {
                list_del(p);
                free(p);
            }
        }
        
        list_for_each(p, head)
        {
            tmp = container_of(p, struct_node, list);
            printf("%d \n", tmp->num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    Makefile

    与上面一致

    结果
    xsndz@Linux:~/Desktop/list$ ./test 
    0 
    2 
    3 
    4 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、list_for_each_entry

    1.功能简介:

      遍历链表的同时可同时获取对应链表的数据结点。

    2.源码及注释:

    /****** 遍历链表的同时获得结构体指针 ******/
    #define list_for_each_entry(p, h, field)                 \
        for ((p) = list_first_entry(h, typeof(*(p)), field); \
             &(p)->field != (h);                             \
             (p) = list_entry((p)->field.next, typeof(*(p)), field))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.源文件测试理解步骤

    main.c
    #include 
    #include 
    #include "list.h"
    #include "dlist_test.h"
    
    /* 链表节点 */
    typedef struct _TestStruct
    {
        struct list_head list;
        int num;
        bool pending;
        char name;
        void *pointer;
    }struct_node;
    
    static struct list_head dlist = LIST_HEAD_INIT(dlist);  /* 初始化链表: */
    
    int main(int argc, char *argv[])
    {
        struct_node *tmp;                                   /* 暂存链表节点结构体 */
        struct list_head *head = &dlist;                    /* 链表表头 */
        int i = 0;
    
        //插入5个数据,使用头插法,类似于栈
        for (i = 0; i < 5; i++)
        {
            tmp = (struct_node *)malloc(sizeof(struct_node));
            tmp->num = i;
            list_add_tail(&tmp->list, head);                /* 插入链表尾部 */
        }
    
        /*
            功能:遍历链表
            参数:
                tmp:用于存储链表结点
                head:头指针
                list:节点中链表的指针
        */
        list_for_each_entry(tmp, head, list)
        {
            printf("%d\n",tmp->num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    Makefile

    与上面一致。

    结果
    xsndz@Linux:~/Desktop/list$ ./test 
    0 
    1
    2 
    3 
    4 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    02_SpringMVC从0开始框架搭建
    计算机组成原理·考点知识点整理
    【车载开发系列】CAN总线知识入门篇
    Dinky上路之旅
    acwing.台阶-nim游戏
    C#: 解释器模式(附完整源码)
    Rasa 多轮对话机器人
    基于事件触发机制的孤岛微电网二次电压与频率协同控制MATLAB仿真模型
    电子信息类毕业生找工作有多难?研究所难进?大厂太卷?
    Revit如何使用插件实现【参数同步】?
  • 原文地址:https://blog.csdn.net/weixin_42645653/article/details/126560076