linux内核常用宏 list_for_each_entry

 list_for_each_entry在内核代码中随处可见。用于遍历链表。下面是我对它的理解。

定义

先看看宏定义:

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)                                                                                                                                                                         

#define list_for_each_entry(pos, head, member)                          \
    for (pos = list_entry((head)->next, typeof((*pos)), member);        \
         &pos->member != (head);                                        \
         pos = list_entry(pos->member.next, typeof((*pos)), member))

乍一看它是一个特定for循环头。由list_entry这个宏组成。

list_entry其实就是container_of宏,实现通过结构体成员获得结构体母体指针的方法。

在之前的文章中已经介绍过,不再赘述。

我还是习惯直接从使用中去学习它。

例子

假设有结构体如下

struct list_head {                                                                                                                                                                                          
    struct list_head *next, *prev;
};

struct node
{
    int val;
    struct list_head my_list;
};

结构体node由my_list和val组成,my_list 是双向链表中的一环,附代码如下

#include<stdio.h>
#include<stdlib.h>

struct list_head {
    struct list_head *next, *prev;
};

#define INIT_LIST_HEAD(ptr) do {  (ptr)->next = (ptr); (ptr)->prev = (ptr); } while (0)

static inline void __list_add(struct list_head *add,
        struct list_head *prev,
        struct list_head *next)
{
    next->prev = add;
    add->next = next;
    add->prev = prev;
    prev->next = add;
}

static inline void list_add_tail(struct list_head *add, struct list_head *head)//每次添加节点都是头结点之前,由于是循环链表,就是说添加到链表尾部
{
    __list_add(add, head->prev, head);
}

#define offsetof(struct_t,member) ((size_t)(char *)&((struct_t *)0)->member)

#define container_of(ptr, type, member) ({                  \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

#define list_for_each_entry(pos, head, member)                          \
    for (pos = list_entry((head)->next, typeof((*pos)), member);        \
         &pos->member != (head);                                        \
         pos = list_entry(pos->member.next, typeof((*pos)), member))

struct node
{
    int val;
    struct list_head my_list;
};

int main()                                                                                                                                                                                                  
{
    struct list_head head;
    struct node a,b,c,*pnode;

    a.val = 1;
    b.val = 2;
    c.val = 3;

    INIT_LIST_HEAD(&head);            //初始化链表头
    list_add_tail(&a.my_list,&head);     //添加节点
    list_add_tail(&b.my_list,&head);
    list_add_tail(&c.my_list,&head);

    printf("*******************************************\n");

    list_for_each_entry(pnode,&head,my_list)//遍历链表,打印结果
    {
        printf("val = %d\n",pnode->val);
    }//print 1 2 3 

    printf("*******************************************\n");
    struct node d,e;
    d.val = 4;
    e.val = 5;
    list_add_tail(&d.my_list,&head);
    list_add_tail(&e.my_list,&head);

    list_for_each_entry(pnode,&head,my_list)
    {
        printf("val = %d\n",pnode->val);
    }//print 1 2 3 4 5

    printf("*******************************************\n");

    return 0;
}

打印如下:

*******************************************
val = 1
val = 2
val = 3
*******************************************
val = 1
val = 2
val = 3
val = 4
val = 5
*******************************************

宏展开

好了,基于上面的例子我们来对宏进行展开

list_for_each_entry(pnode,&head,my_list)//遍历链表,打印结果
{
    printf("val = %d\n",pnode->val);
}

//宏展开如下
for(pnode = list_entry((&head)->next), typeof(* pnode),my_list);      \
    &pnode->mylist != head;                                           \
    pnode = list_entry((pnode->mylist).next, typeof(* pnode),my_list)
{
    printf("val = %d\n",pnode->val);
} 

//伪代码
for(pnode=head->next的母结构体node;
    当pnode->mylist的下一个链表为head时退出循环;
    pnode=(pnode->mylist).next的母结构体node;)
{
     printf("val = %d\n",pnode->val);
}

其实 宏的第一个参数 pnode只是一个暂存器,用来遍历链表上所以的node节点。

其实 宏的第二个参数 head 是链表头的名字

其实 宏的第三个参数 my_list 是node结构体中链表成员的名字

嘿嘿,你会用了吗?

宏的实现功能就是从链表头head的下一个节点开始遍历链表的母结构体node(存于pnode中)直至链表头head的前一个节点为止。

猜你喜欢

转载自blog.csdn.net/a827143452/article/details/85929809
今日推荐