linux 驱动学习之list链表机制

转载:http://blog.chinaunix.net/uid-27122224-id-3277511.html

双向链表

struct list_head{
      struct list_head *next, *prev;
};
struct dlist{
     int no;
     void* data;
     struct dlist *prev, *next;
    };

结构:
这里写图片描述

struct list_head结构的操作

1.初始化

就是用head的地址初始化其两个成员next和prev ,使其都指向自己。

    #define LIST_HEAD_INIT(name) { &(name), &(name) }
    #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
    #define INIT_LIST_HEAD(ptr) do { \
          (ptr)->next = (ptr); (ptr)->prev = (ptr); \
    } while (0)

具体使用:
struct list_head head;
LIST_HEAD_INIT(head);
LIST_HEAD(head);

2.增加节点的函数

相关api:
    static inline void __list_add();
    static inline void list_add();
    static inline void list_add_tail();

具体实现:
    /**
     * __list_add - Insert a new entry between two known consecutive entries.
     * @new:
     * @prev:
     * @next:
     *
     * This is only for internal list manipulation where we know the prev/next
     * entries
     */
    static __inline__ void __list_add(struct list_head * new,
                 struct list_head * prev, struct list_head * next)
    {
           next->prev = new;
           new->next = next;
           new->prev = prev;
           prev->next = new;
    }
    //这个函数在prev和next间插入一个节点new。

    /**
     * list_add - add a new entry
     * @new: new entry to be added
     * @head: list head to add it after
     *
     * Insert a new entry after the specified head.
     * This is good for implementing stacks.
     */
         static __inline__ void list_add(struct list_head *new, struct list_head *head)
    {
           __list_add(new, head, head->next);
    }
    //这个函数在head节点后面插入new节点。

    /**
     * list_add_tail - add a new entry
     * @new: new entry to be added
     * @head: list head to add it before
     *
     * Insert a new entry before the specified head.
     * This is useful for implementing queues.
     */
    static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
    {
           __list_add(new, head->prev, head);
    }

3.删除节点的函数

1.list_del(struct list_head *entry)是从链表中删除entry节点。 
2.list_del_init(struct list_head *entry) 不但从链表中删除节点,还把这个节点的向前向后指针都指 
向自己,即初始化。

具体实现:
    /**
     * __list_del -
     * @prev:
     * @next:
     *
     * Delete a list entry by making the prev/next entries point to each other.
     *
     * This is only for internal list manipulation where we know the prev/next
     * entries
     */
    static __inline__ void __list_del(struct list_head * prev,
                 struct list_head * next)
    {
           next->prev = prev;
           prev->next = next;
    }

    /**
     * list_del - deletes entry from list.
     * @entry: the element to delete from the list.
     * * Note: list_empty on entry does not return true after this, the entry is in
     * an undefined state.
     */
    static __inline__ void list_del(struct list_head *entry)
    {
          __list_del(entry->prev, entry->next);
    }

    /**
     * list_del_init - deletes entry from list and reinitialize it.
     * @entry: the element to delete from the list.
     */
    static __inline__ void list_del_init(struct list_head *entry)
    {
          __list_del(entry->prev, entry->next);
           INIT_LIST_HEAD(entry);
    }

3.判断链表是否为空

    /**
     * list_empty - tests whether a list is empty
     * @head: the list to test.
     */
    static __inline__ int list_empty(struct list_head *head)
    {
          return head->next == head;
    }

4.通过list_head变量来获取整个结构的变量

    /**
     * list_entry - get the struct for this entry
     * @ptr: the &struct list_head pointer.
     * @type: the type of the struct this is embedded in.
     * @member: the name of the list_struct within the struct.
     */
    #define list_entry(ptr, type, member) \ 
         ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

举例来说:
struct person *one = list_entry(pos, struct person, list);
把list_entry 展开
((struct person )((char )(pos) - (unsigned long)(&((struct person *)0)->list)))
其中
1.(unsigned long)(&((struct person *)0)->list)的意思就是取list 变量在struct person结构中的偏移量。

2.((char )(pos) - (unsigned long)(&((struct person )0)->list))
就是将pos指针往前移动offset位置,即是本来pos是struct list_head类型,它即是list。即是把
pos指针往struct person结构的头地址位置移动过去,如上图的pos和虚箭头。
当pos移到struct person结构头后就转 成(struct person *)指针,这样就可以得到struct person
*变量了。
这里写图片描述

5.循环遍历双向链表

    /**
     * list_for_each - iterate over a list
     * @pos: the &struct list_head to use as a loop counter.
     * @head: the head for your list.
     */
    #define list_for_each(pos, head) \
          for (pos = (head)->next; pos != (head); pos = pos->next)

    /**
     * list_for_each_safe - iterate over a list safe against removal of list entry
     * @pos: the &struct list_head to use as a loop counter.
     * @n: another &struct list_head to use as temporary storage
     * @head: the head for your list.
     */
    #define list_for_each_safe(pos, n, head) \
          for (pos = (head)->next, n = pos->next; pos != (head); \
                pos = n, n = pos->next)

list_for_each(pos, head)是遍历整个head链表中的每个元素,每个元素都用pos指向。

list_for_each_safe(pos, n, head)是用于删除链表head中的元素,不是上面有删除链表元素的函数了
吗,为什么这里又要定义一个这样的宏呢。看下这个宏后面有个safe字,就是说用这个宏来删除是安全的,
直接用前面的那些删除函数是不安全的。

原因:
list_for_each
删除pos后,list_for_each要移到下一个元素,还需要用pos来取得下一个元素,但pos的指向已 经改变,如果不直接退出而是在继续操作的话,就会出错了。
ist_for_each_safe
这里我们使用了n作为一个临时的指针,当pos被删除后,还可以用n来获得下一个元素的位置。

6.实现举例

    #include <stdio.h>
    #include "list.h"

    struct person
    {
          int age;

          int weight;
          struct list_head list;
    };

    int main(int argc, char* argv[])
    {
          struct person *tmp;
          struct list_head *pos, *n;
          int age_i, weight_j;

          // 定义并初始化一个链表头
          struct person person_head;
          INIT_LIST_HEAD(&person_head.list);

          for(age_i = 10, weight_j = 35; age_i < 40; age_i += 5, weight_j += 5)
          {
                tmp =(struct person*)malloc(sizeof(struct person));
                tmp->age = age_i;
                tmp->weight = weight_j;

                // 把这个节点链接到链表后面
                // 这里因为每次的节点都是加在person_head的后面,所以先加进来的节点就在链表里的最
    后面

                // 打印的时候看到的顺序就是先加进来的就在最后面打印
                list_add(&(tmp->list), &(person_head.list));

          }

          // 下面把这个链表中各个节点的值打印出来
          printf("\n");
          printf("=========== print the list ===============\n");
          list_for_each(pos, &person_head.list)
          {
                // 这里我们用list_entry来取得pos所在的结构的指针
                tmp = list_entry(pos, struct person, list);
                printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
          }
          printf("\n");

          // 下面删除一个节点中,age为20的节点
          printf("========== print list after delete a node which age is 20
    ==========\n");
          list_for_each_safe(pos, n, &person_head.list)
          {

            tmp = list_entry(pos, struct person, list);
                  if(tmp->age == 20)
                  {
                        list_del_init(pos);
                        free(tmp);
                  }

           }

           list_for_each(pos, &person_head.list)
           {
                  tmp = list_entry(pos, struct person, list);
                  printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
           }

           // 释放资源
           list_for_each_safe(pos, n, &person_head.list)
           {
                  tmp = list_entry(pos, struct person, list);
                  list_del_init(pos);
                  free(tmp);
           }

           return 0;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值