双向链表的数据结构使用


https://www.cnblogs.com/skywang12345/p/3562146.html   【详细源自于】

https://tool.lu/coderunner/   【在线编译】



在linux内核中,有大量的数据结构需要用到双循环链表,
例如进程、文件、模块、页面等。若采用双循环链表的传统实现方式,
需要为这些数据结构维护各自的链表,并且为每个链表都要设计插入、删除等操作函数。
因为用来维持链表的next和prev指针指向对应类型的对象,
因此一种数据结构的链表操作函数不能用于操作其它数据结构的链表。

===========这样操作是为了 通用 Common============

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



#ifndef _LIST_HEAD_H
#define _LIST_HEAD_H

// 双向链表节点
struct list_head {
    struct list_head *next, *prev;
};

// 初始化节点:设置name节点的前继节点和后继节点都是指向name本身。
#define LIST_HEAD_INIT(name) { &(name), &(name) }
//  等价于  struct list_head {struct list_head *next, *prev; } foo = { &(foo) , &(foo)};


// 定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

// 初始化节点:将list节点的前继节点和后继节点都是指向list本身。
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

// 添加节点:将new插入到prev和next之间。
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;
}

// 添加new节点:将new添加到head之后,是new称为head的后继节点。
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}

// 添加new节点:将new添加到head之前,即将new添加到双链表的末尾。
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}

// 从双链表中删除entry节点。
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}

// 从双链表中删除entry节点。
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
}

// 从双链表中删除entry节点。
static inline void __list_del_entry(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
}

// 从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。
static inline void list_del_init(struct list_head *entry)
{
    __list_del_entry(entry);
    INIT_LIST_HEAD(entry);
}

// 用new节点取代old节点
static inline void list_replace(struct list_head *old,
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}

// 双链表是否为空
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
}

// 获取"MEMBER成员"在"结构体TYPE"中的位置偏移
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

// 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

// 遍历双向链表
#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)

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

#endif
	
	
	
	
	
	
struct person 
{ 
    int age; 
    char name[20];
    struct list_head list; 
};

void main(int argc, char* argv[]) 
{ 
    struct person *pperson; 
    struct person person_head; 
    struct list_head *pos, *next; 
    int i;

    // 初始化双链表的表头 
    INIT_LIST_HEAD(&person_head.list); 

    // 添加节点
    for (i=0; i<5; i++)
    {
        pperson = (struct person*)malloc(sizeof(struct person));
        pperson->age = (i+1)*10;
        sprintf(pperson->name, "%d", i+1);
        // 将节点链接到链表的末尾 
        // 如果想把节点链接到链表的表头后面,则使用 list_add
        list_add_tail(&(pperson->list), &(person_head.list));
    }

    // 遍历链表
    printf("==== 1st iterator d-link ====\n"); 
    list_for_each(pos, &person_head.list) 
    { 
        pperson = list_entry(pos, struct person, list); 
        printf("name:%-2s, age:%d\n", pperson->name, pperson->age); 
    } 

    // 删除节点age为20的节点
    printf("==== delete node(age:20) ====\n");
    list_for_each_safe(pos, next, &person_head.list)
    {
        pperson = list_entry(pos, struct person, list);
        if(pperson->age == 20)
        {
            list_del_init(pos);
            free(pperson);
        }
    }

    // 再次遍历链表
    printf("==== 2nd iterator d-link ====\n");
    list_for_each(pos, &person_head.list)
    {
        pperson = list_entry(pos, struct person, list);
        printf("name:%-2s, age:%d\n", pperson->name, pperson->age);
    }

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

输出:

==== 1st iterator d-link ====
name:1 , age:10
name:2 , age:20
name:3 , age:30
name:4 , age:40
name:5 , age:50
==== delete node(age:20) ====
==== 2nd iterator d-link ====
name:1 , age:10
name:3 , age:30
name:4 , age:40
name:5 , age:50

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、linux内核链表 1、普通链表的数据区域的局限性 之前定义数据区域时直接int data,我们认为我们的链表中需要存储的是一个int类型的数。但是实际上现实编程中链接中的节点不可能这么简单,而是多种多样的。 一般实际项目中的链表,节点中存储的数据其实是一个结构体,这个结构体中包含若干的成员,这些成员加起来构成了我们的节点数据区域。 2、一般性解决思路:即把数据区封装为一个结构体 (1)因为链表实际解决的问题是多种多样的,所以内部数据区域的结构体构成也是多种多样的。 这样也导致了不同程序当中的链表总体构成是多种多样的。 我们无法通过一套泛性的、普遍适用的操作函数来访问所有的链表,意味着我们设计一个链表就得写一套链表的操作函数(节点创建、插入、删除、遍历……)。 (2)实际上深层次分析会发现 不同的链表虽然这些方法不能通用需要单独写,但是实际上内部的思路和方法是相同的,只是函数的局部地区有不同。 实际上链表操作是相同的,而涉及到数据区域的操作就有不同 (3)问题 能不能有一种办法把所有链表中操作方法里共同的部分提取出来用一套标准方法实现,然后把不同的部分留着让具体链表的实现者自己去处理。 3、内核链表的设计思路 (1)内核链表中实现一个纯链表的封装,以及纯链表的各种操作函数 纯链表就是没有数据区域,只有前后向指针; 各种操作函数是节点创建、插入、删除、遍历。 这个纯链表本身自己没有任何用处,它的用法是给我们具体链表作为核心来调用。 4、list.h文件简介 (1)内核中核心纯链表的实现在include/linux/list.h文件中 (2)list.h中就是一个纯链表的完整封装,包含节点定义和各种链表操作方法。 二、内核链表的基本算法和使用简介 1、内核链表的节点创建、删除、遍历等 2、内核链表的使用实践 (1)问题:内核链表只有纯链表,没有数据区域,怎么使用使用方法是将内核链表作为将来整个数据结构的结构体的一个成员内嵌进去。类似于公司收购,实现被收购公司的功能。 这里面要借助container_of宏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值