c语言内核链表

c语言内核链表

在Linux中拥有大量的内核源码,在数据存储的这块位置拥有内核链表(双向循环链表)
由linux内核提供的链表文件,里面包含了多组内联函数和宏定义函数以及功能性函数。
内核链表中定义了多个函数,我们只需要知道参数的类型,然后填入参数,即可完成增删改查操作。
内核链表兼容了通用性,将指针域剥离出来,只封装指针域来管理节点。
内核链表优缺点:
优点:逻辑完整,函数封装完整,只需要调用,指针域独立管理节点。
缺点:用户无法修改内核链表的写法。

在这里插入图片描述

实现内核链表的使用

#include <stdio.h>
#include <stdlib.h>
#include "list.h"typedef struct node
{int data;               // 数据域// 小结构体  这里为什么最好不用指针是因为内核函数里面有一个参数是取名字,并且初始化的时候取得是名字的地址&(name)struct list_head xNode;
} DNode, *DNODE;// 初始化头结点
DNODE initHead()
{// 申请空间DNODE Dhead = malloc(sizeof(DNode));if (Dhead == NULL){printf("大结构体空间申请失败!\n");return NULL;}// 初始化指针域INIT_LIST_HEAD(&(Dhead->xNode));return Dhead;
}
// 初始化普通节点
DNODE initNode(int data)
{// 申请空间DNODE xNode = malloc(sizeof(DNode));if (xNode == NULL){printf("小结构体空间申请失败!\n");return NULL;}// 初始化数据域xNode->data = data;// 初始化指针域INIT_LIST_HEAD(&(xNode->xNode));return xNode;
}// 插入节点
void insertNode(DNODE Dhead, DNODE xnode)
{// 插入头节点之前// list_add(&(xnode->xNode), &(Dhead->xNode));// 插入头节点之后list_add_tail(&(xnode->xNode), &(Dhead->xNode));
}// 遍历链表
void showList(DNODE Dhead)
{// 两种写法遍历链表struct list_head *pos, *safe_pos; // 小结构体指针DNODE safe_node, node;            // 大结构体指针printf("head->");// 正向遍历 list_for_each// list_for_each(pos, &(Dhead->xNode))// 逆向遍历  list_for_each_prev// list_for_each_prev(pos, &(Dhead->xNode))// 正向安全遍历// list_for_each_safe(pos, safe_pos, &(Dhead->xNode))// // 逆向安全遍历// // list_for_each_prev_safe(pos, safe_node, &(Dhead->xNode))// {//     /*//      * 通过list_entry方法放入小结构体地址获取到大结构体地址//      * pos小结构体地址//      * DNode大结构体类型//      * xNode小结构体名字//      *///     DNODE newName = list_entry(pos, DNode, xNode);//     printf("%d->", newName->data);// }/*** 而安全跟不安全的区别就是,不安全的不支持遍历删除,如果在循环内部删除了当前节点,可能会导致遍历指针 (pos) 指向一个已经被删除的节点,从而引发未定义行为。* 上下两种遍历方式区别* 跟上面正向的却别就是上面方法需要通过小结构体找到大结构体再通过大结构体访问数据* 而下面这种方法遍历访问数据直接就通过大结构体访问*/// 大结构体正向遍历//  list_for_each_entry(safe_node, &(Dhead->xNode), xNode)//  正向安全list_for_each_entry_safe(safe_node, node, &(Dhead->xNode), xNode){/** 通过list_entry方法放入小结构体地址获取到大结构体地址* pos小结构体地址* DNode大结构体类型* safe_node 大结构体节点*/printf("%d->", safe_node->data);}printf("\n");
}// 删除节点
void del_node(DNODE Dhead, int data)
{// 赋初始值为头结点地址DNODE pos = Dhead;// 找到符合条件的大结构体节点list_for_each_entry(pos, &(Dhead->xNode), xNode){if (pos->data == data)break;}// if (pos != Dhead)// 如果链表非空if (!list_empty(&(pos->xNode))){list_del(&(pos->xNode));}
}// 查找节点
DNODE find_node(DNODE Dhead, DNODE newFindNode)
{DNODE pos = Dhead;// 找到符合条件的大结构体节点list_for_each_entry(pos, &(Dhead->xNode), xNode){if (pos->data == newFindNode->data)break;}// 如果找到就返回,否则返回头结点return pos != Dhead ? pos : NULL;
}// 移动节点newNode移动节点,newNode2目标节点
void move_node(DNODE head, DNODE newNode1, DNODE newNode2)
{DNODE pos = find_node(head, newNode1);if (pos == head){return;}DNODE move_to_pos = find_node(head, newNode2);if (move_to_pos == head){return;}/***   参数1要移动的节点,参数2目标节点*  这里移动是移动到目标节点的头结点之前*/// 移到某某节点之前//  list_move(&(pos->xNode), &(move_to_pos->xNode));//  移到某某节点之后list_move_tail(&(pos->xNode), &(move_to_pos->xNode));
}// 修改节点
void updata_node(DNODE Dhead, DNODE newNode, DNODE newNode2)
{DNODE pos = find_node(Dhead, newNode);if (pos == Dhead){return;}pos->data = newNode2->data;
}int main()
{// 初始化头结点DNODE Dhead = initHead();// 生成5个节点插入进去for (int i = 0; i < 4; i++){// 初始化数据域DNODE xnode = initNode(i);insertNode(Dhead, xnode);}DNODE Dhead1 = initHead();for (int i = 6; i < 12; i++){// 初始化数据域DNODE xnode = initNode(i);insertNode(Dhead1, xnode);}del_node(Dhead, 1);showList(Dhead);// 原链表不清除list_splice(&(Dhead1->xNode), &(Dhead->xNode));// 这里指针乱了,所以将指针清空,最好把它释放掉Dhead1 = NULL;free(Dhead1);// 原链表清除// list_splice_init(&(Dhead1->xNode), &(Dhead->xNode));showList(Dhead);// 创建要查找的节点DNODE newFindNode = initNode(11);DNODE newNode = find_node(Dhead, newFindNode);// printf("%d\n", newNode->data);// 移动节点DNODE moveNode1 = initNode(7);DNODE moveNode2 = initNode(2);move_node(Dhead, moveNode1, moveNode2);showList(Dhead);// 修改节点DNODE upDateNode = initNode(2);DNODE upDateNode2 = initNode(200);updata_node(Dhead, upDateNode, upDateNode2);showList(Dhead);return 0;
}

内核链表一些方法源码


#ifndef __DLIST_H
#define __DLIST_H#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({			\const typeof( ((type *)0)->member ) *__mptr = (ptr);	\(type *)( (char *)__mptr - offsetof(type,member) ); })#define LIST_POISON1 ((void *)0x00100100)
#define LIST_POISON2 ((void *)0x00200)// 创建指针域结构体
struct list_head
{struct list_head *next, *prev;
};#define LIST_HEAD_INIT(name) {&(name), &(name)}#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)/** ptr 小结构体的指针* 初始化的时候让他们的前驱后链指向自己*/
#define INIT_LIST_HEAD(ptr)  \do                       \{                        \(ptr)->next = (ptr); \(ptr)->prev = (ptr); \} while (0)/*** 插入一个节点到链表中需要重新指向的前驱后链交换 静态方法*/
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 当前结构体的小结构体* head头结点的小结构体*/
static inline void list_add(struct list_head *new, struct list_head *head)
{__list_add(new, head, head->next);
}/***倒链*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
}// 删除方法
static inline void __list_del(struct list_head *prev, struct list_head *next)
{next->prev = prev;prev->next = next;
}/*** 删除节点 静态方法* 要删除数据的小结构体*/
static inline void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = (void *)0;entry->prev = (void *)0;
}/*** 删除节点,又把删除的节点重新创建,只是没有链接*/
static inline void list_del_init(struct list_head *entry)
{__list_del(entry->prev, entry->next);INIT_LIST_HEAD(entry);
}/*** 这里移动是移动到目标节点的头结点之后*/
static inline void list_move(struct list_head *list,struct list_head *head)
{__list_del(list->prev, list->next);list_add(list, head);
}/*** 这里移动是移动到目标节点的头结点之后*/
static inline void list_move_tail(struct list_head *list,struct list_head *head)
{__list_del(list->prev, list->next);list_add_tail(list, head);
}/*** 判断链表是否为空*/
static inline int list_empty(struct list_head *head)
{return head->next == head;
}static inline void __list_splice(struct list_head *list,struct list_head *head)
{struct list_head *first = list->next;struct list_head *last = list->prev;struct list_head *at = head->next;first->prev = head;head->next = first;last->next = at;at->prev = last;
}/*** 合并链表,不清空原链表*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{if (!list_empty(list))__list_splice(list, head);
}/*** 合并链表,并清空被合并的链表*/
static inline void list_splice_init(struct list_head *list,struct list_head *head)
{if (!list_empty(list)){__list_splice(list, head);INIT_LIST_HEAD(list);}
}/*** 通过小结构体地址获取大结构体地址*/
#define list_entry(ptr, type, member) \((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))/*** 正向插入*/#define list_for_each(pos, head)            \for (pos = (head)->next; pos != (head); \pos = pos->next)
/*** 逆向插入*/
#define list_for_each_prev(pos, head)       \for (pos = (head)->prev; pos != (head); \pos = pos->prev)/****/
#define list_for_each_safe(pos, n, head)                   \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)/********往前遍历(安全模式)************/
#define list_for_each_prev_safe(pos, n, head)              \for (pos = (head)->prev, n = pos->prev; pos != (head); \pos = n, n = pos->prev)/*** 大结构体遍历*/
#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))/*** 大结构体安全遍历*/
#define list_for_each_entry_safe(pos, n, head, member)          \for (pos = list_entry((head)->next, typeof(*pos), member),  \n = list_entry(pos->member.next, typeof(*pos), member); \&pos->member != (head);                                \pos = n, n = list_entry(n->member.next, typeof(*n), member))#endif

总结方法

  • list_head 指针域结构体名 内核固定写死的不能修改
  • LIST_HEAD_INIT:初始化链表头 struct list_head my_list = LIST_HEAD_INIT(my_list); 只不过这里没有给前驱后链后面自己给就行
  • INIT_LIST_HEAD(小结构体名) 创建单个独立的结构体指针 ,创建出来的都是单个的至于哪个做头结点自己标记下,后面节点需要给上数据域
    – 1.如果要创建头结点 INIT_LIST_HEAD(&(Dhead->xNode)); 开辟空间后的头结点的指针域
    – 2.如果要创建后面节点 INIT_LIST_HEAD(&(xNode->xNode)); 开辟空间后每个结构的指针域,还要给上数据域
  • 插入数据参数1:上面标记好的头结点作为链表头地址 ,, 参数2:要插入的节点)
    list_add头插 (参数1:插入的节点的指针域,, 参数2:头结点的指针域)list_add(&(xnode->xNode), &(Dhead->xNode)); 取地址
    list_add_tail 尾插参照头插
  • 遍历数据
    list_for_each(pos, &(Dhead->xNode)) 正向遍历
    list_for_each_prev(pos, &(Dhead->xNode))逆向遍历
    list_for_each_safe(pos, safe_pos, &(Dhead->xNode))正向安全遍历
    list_for_each_prev_safe(pos, safe_node, &(Dhead->xNode))逆向安全遍历
    list_for_each_entry(safe_node, &(Dhead->xNode), xNode) 大结构体正向遍历
    list_for_each_entry_safe(safe_node, node, &(Dhead->xNode), xNode) 大结构体正向安全遍历
    安全定义:安全跟不安全的区别就是,不安全的不支持遍历删除,如果在循环内部删除了当前节点,可能会导致遍历指针 (pos) 指向一个已经被删除的节点,从而引发未定义行为。
  • 删除数据list_del(&(pos->xNode)); list_del_init 删除节点并重新注册个
  • 合并列表
    list_splice(&(Dhead1->xNode), &(Dhead->xNode)); 原链表不清除
    list_splice_init(&(Dhead1->xNode), &(Dhead->xNode));原链表清除
    判断链表是否为空 list_empty(&(pos->xNode))
    移动
    list_move(&(pos->xNode), &(move_to_pos->xNode)); 参数1要移动的节点,参数2目标节点, 这里移动是移动到目标节点的头结点之前
    list_move_tail(&(pos->xNode), &(move_to_pos->xNode));这里移动是移动到目标节点的头结点之后

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/455791.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(gersemi) CMake 格式化工具

文章目录 &#x1f9ee;介绍&#x1f9ee;安装&#x1f9ee;使用&#x1f5f3;️模式 modes&#x1f5f3;️样式配置 config ⭐END&#x1f31f;help&#x1f31f;交流方式 &#x1f9ee;介绍 BlankSpruce/gersemi: A formatter to make your CMake code the real treasure A f…

关闭或开启Win11系统的自动更新

Win11系统老是自动更新&#xff0c;每次更新后不仅拖慢计算机的运行速度&#xff0c;甚至打印机都无法使用了&#xff0c;给我们带来了很多困扰。 那么我们该如何彻底关闭Win11系统的自动更新呢&#xff1f;关闭Win11系统自动更新会有什么弊端呢&#xff1f; 下面就分享几个小方…

NVIDIA 发布适用于网络安全的 NIM Blueprint

德勤使用适用于容器安全的 NVIDIA NIM Agent Blueprint 帮助企业利用开源软件构建安全的 AI。 文章目录 &#x1f64a; 德勤使用 NVIDIA AI 保障软件安全&#x1f64a; 通过生成式 AI 保障软件安全&#x1f64a; 适用于网络安全成功的蓝图&#x1f3a0; 什么是 NVIDIA NIM Agen…

ESP32移植Openharmony外设篇(3)OLED屏

模块简介 产品介绍 OLED (Organic Light-Emitting Diode)&#xff1a;有机发光二极管又称为有机电激光显示&#xff0c;OLED显示技术具有自发光的特性&#xff0c;采用薄的有机材料涂层和玻璃基板&#xff0c;当有电流通过时&#xff0c;这些有机材料就会发光&#xff0c;而且…

数组中的算法

目录 1.什么是数组 2.数组上的算法 2.1二分查找算法 什么是二分查找算法&#xff1f; 算法步骤 算法时间复杂度 一个问题 例题 题目分析 解题代码 2.2双指针法 什么是双指针法&#xff1f; 例题 题目分析 解题代码 1.什么是数组 数组是在一块连续的内存空间…

【vuejs】富文本框输入的字符串按规则解析填充表单

今天遇到一个批量添加信息的需求&#xff0c;按照格式要求解析后填充到表单中&#xff0c;不符合规则的直接过滤掉 注&#xff1a;添加的信息都是随机生成&#xff0c;不用于实际用途 这是弹框输入的文本解析代码 export const editValToArr (value, bankArr) > {return n…

vue2-render:vue2项目使用render / 基础使用

一、本文内容 本文内容记录render常用的一些属性和方法的配置&#xff0c;以作参考 export default { data() {return { modelValue: ,key: 0,}; }, render(h) { return h(div, [ h(input, {class: input,attrs: { type: text }, key: this.key,props: { value: thi…

【mysql进阶】2-4. mysql 系统库

mysql System Schema (mysql系统库) Mysql Schema是⼀个系统库&#xff0c;表中存储了MySQL服务器运⾏时所需的信息。⼴义上&#xff0c;mysqlschema包含存储数据库对象元数据的数据字典和⽤于其他操作⽬的的系统表。数据字典表和系统表位于数据⽬录下⼀个名为 mysql.ibd 的表…

“声音”音源设置和音效播放

学习如何使用音效系统&#xff0c;背景音乐和其他特别的音效&#xff0c;跳跃攻击等等 学习如何在unity当中使用整套的音效系统&#xff0c;使用之前&#xff0c;我们先来确定一下我们要使用的音乐和音效&#xff0c;在Unity Asset Store当中搜索&#xff0c;添加到我们的unit…

详解Oracle审计(二)

题记&#xff1a; 本文将承接上篇详细介绍oracle的审计功能&#xff0c;基于11g版本&#xff0c;但对12c&#xff0c;19c也同样适用。 1. 语句审计实操演示实例 sqlplus / as sysdba show parameter audit_trail alter system set audit_traildb_extended scopespfile; star…

OpenCV和HALCON

OpenCV和HALCON是两种广泛用于图像处理和计算机视觉的开发库&#xff0c;它们各有优缺点&#xff0c;适合不同的应用场景。以下是两者的比较&#xff1a; 1. 开发背景与定位 OpenCV (Open Source Computer Vision Library)&#xff1a; 开源库&#xff0c;最初由Intel开发&…

Gitlab 完全卸载–亲测可行

1、停止gitlab gitlab-ctl stop2.卸载gitlab&#xff08;注意这里写的是gitlab-ce&#xff09; rpm -e gitlab-ce 3、查看gitlab进程 ps aux | grep gitlab 4、杀掉第一个进程&#xff08;就是带有好多.............的进程&#xff09; 5、删除所有包含gitlab文件 find / …

【计网】深入理解网络通信:端口号、Socket编程及编程接口

目录 1.端口号 1.1.理解源 IP 地址和目的 IP 地址 1.2.认识端口号 1.3.端口号范围划分 1.4理解 "端口号" 和 "进程 ID" 2.socket编程 2.1.理解 socket 2.2.socket编程的概念 2.3. 传输层的典型代表 认识 TCP 协议 认识 UDP 协议 2.3 网络字节序…

基于Multisim的水位测量电路设计与仿真

1.利用LED指示灯显示水位&#xff08;最低水位、1/4、1/2、3/4、最高水位&#xff09;。 2.达到最高水位时&#xff0c;自动报警。

探索Python与Excel的无缝对接:xlwings库的神秘面纱

文章目录 探索Python与Excel的无缝对接&#xff1a;xlwings库的神秘面纱1. 背景介绍&#xff1a;为何选择xlwings&#xff1f;2. xlwings是什么&#xff1f;3. 如何安装xlwings&#xff1f;4. 简单的库函数使用方法打开工作簿创建工作簿读取单元格数据写入单元格数据保存并关闭…

消息会话—发送消息自动滚动到最底部

背景 在项目开发中&#xff0c;实现用户友好的输入交互是提升用户体验的关键之一。例如&#xff0c;在消息会话页面中&#xff0c;为了确保用户在发送新消息后页面能自动滚动到最底部&#xff0c;从而始终保持最新消息的可见性&#xff0c;需要实现自动滚动功能。这不仅提升了…

Spring Boot集成:高效论坛网站的构建

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理论坛网站的相关信息成为必然。开发合适的论…

【GISBox使用指南】免费实现影像切片的工具,还支持多种格式服务发布!

一、什么是影像数据&#xff1f; 在地理信息系统中&#xff0c;影像数据是指通过遥感技术、摄影测量或其他成像手段获取的&#xff0c;以数字形式存储的地理空间图像信息。这些数据涵盖了从卫星遥感影像、航空摄影影像到地面摄影影像等多种类型&#xff0c;在GIS中的应用广泛而…

知乎付费投流怎么做?如何投放知乎广告?

知识经济背景下&#xff0c;知乎凭借其高质量的内容和精准的用户群体&#xff0c;成为了品牌营销的新蓝海。作为国内领先的知识分享平台&#xff0c;知乎汇聚了大量高学历、高收入、高消费能力的用户&#xff0c;他们对新知识、新产品有着强烈的好奇心和探索欲&#xff0c;是品…

成功解决pycharm软件中按住Ctrl+点击指定函数却不能跳转到对应库中的源代码

成功解决pycharm软件中按住Ctrl点击指定函数却不能跳转到对应库中的源代码 目录 解决问题 解决方法 解决问题 在pycharm软件中按住Ctrl点击指定函数却不能跳转到对应库中的源代码 解决方法