PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=020)

  本文发布于 2016-06-06 14:49:11,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=020)

环境说明

  无

前言


  无

入侵式链表


  链表按照实现原理我分为两类,一是普通的链表,二是入侵式链表。(区别就是,链表结构体里面有没有数据)。由于我重写了Linux 链表的实现,部分东西被我更改,所以文中代码以我的实际代码为例。

下面围绕几点来理解一下:

  链表元素结构体声明:

//
typedef struct y_list_head {
struct y_list_head *next;
struct y_list_head *prev;
} Y_LISTHEAD;

  节点元素结构体声明:

//
typedef struct {
int a;
Y_LISTHEAD list;
} test;

  链表的头节点(用于存放一个链表的逻辑头,由于链表是双向,每个节点都是头):

//declare a node for head of list .
#define LIST_HEAD(name) \
Y_LISTHEAD name = LIST_HEAD_INIT(name)

  这里为了展示例子,我申请了几个变量:

test* ptr_test;
Y_LISTHEAD * pos;
test * entry;

  为节点申请内存,并赋值

ptr_test = (test *)malloc(sizeof(test));
ptr_test->a = 0;

  现在节点创建完毕,开始加入到链表

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(Y_LISTHEAD *new,
Y_LISTHEAD *prev,
Y_LISTHEAD *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
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(Y_LISTHEAD *new, Y_LISTHEAD *head)
{
__list_add(new, head, head->next);
} LIST_ADD(&ptr_test->list,&test_list);

  同理,在添加几个。(详情见末尾例子),然后,遍历链表并输出:

// 这里解释一下 GET_ENTRY()宏函数
//caculate offset-value about member to start of the struct
//计算结构体地址和结构体某成员的地址差值,也就是偏移值
#define OFFSET(type, member) ((unsigned int) &((type *)0)->member) //第一行声明了一个结构体元素类型的指针
//第二行通过偏移值计算结构体地址,这样就可以得到结构体内所有元素的值
//第三个参数member 为节点元素中,链表变量名
//第二个参数为节点元素类型名
#define GET_ENTRY(ptr, type, member) \
({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - OFFSET(type,member) ); \
})
/**
* list_for_each iterate over a list
* @pos:the &struct list_head to use as a loop cursor.
* @head:the head for your list.
*/
#define LIST_FOR_EACH(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next) LIST_FOR_EACH(pos,&test_list){ entry = GET_ENTRY(pos,test,list);
printf("%d\n",entry->a);
}
// 同理, LIST_DEL_ENTRY()之类的宏定义都是同样的实现。

  实例:list_test.c

/*
* FileName:list_test.c
* Version: 1.0
* Description: The file test my list if it is right
* Created On:2016-5-15
* Modified date:
* Author:Sky
*
*/
#include <stdio.h>
#include "Y_List/Y_List.h" typedef struct {
int a;
Y_LISTHEAD list;
} test; test* ptr_test; int main(int argc, char * argv[]){ //test head; LIST_HEAD(test_list);
Y_LISTHEAD * pos;
test * entry; //INIT_LIST_HEAD(head->list); ptr_test = (test *)malloc(sizeof(test));
ptr_test->a = 0; LIST_ADD(&ptr_test->list,&test_list); ptr_test = (test *)malloc(sizeof(test));
ptr_test->a = 2; LIST_ADD_TAIL(&ptr_test->list,&test_list); ptr_test = (test *)malloc(sizeof(test));
ptr_test->a = 4; LIST_ADD(&ptr_test->list,&test_list); ptr_test = (test *)malloc(sizeof(test));
ptr_test->a = 8; LIST_ADD_TAIL(&ptr_test->list,&test_list); LIST_FOR_EACH(pos,&test_list){ entry = GET_ENTRY(pos,test,list);
printf("%d\n",entry->a);
} LIST_DEL_ENTRY(&(ptr_test->list)); LIST_FOR_EACH(pos,&test_list){ entry = GET_ENTRY(pos,test,list);
printf("%d\n",entry->a);
} LIST_FOR_EACH(pos,&test_list){ entry = GET_ENTRY(pos,test,list);
free(entry);
} return 0;
} // LIST_ADD在给出地址后添加一个元素
// LIST_ADD_TAIL在给出地址前添加一个元素
// LIST_DEL_ENTRY删除指定地址的节点

  运行截图:

  其实链表只有几个部分要主要:

  • 链表结构体

  • 节点结构体

  • 链表头

  • 链表增删改查

  而在Linux的链表实现中,要注意下面这个东西,怎么确定的节点结构体地址,这是LInux链表实现的核心。搞清楚了这个,Linux链表你就会了GET_ENTRY()

  如果希望查看上面项目代码,移步:https://github.com/flyinskyin2013/Y_LINUX_FRAME/tree/master/include/Y_List

后记


  无

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

LinuxKernel 入侵式双向链表的设计,分析,使用的更多相关文章

  1. [课程设计]Scrum 2.5 多鱼点餐系统开发进度(下单一览页面-菜式添加框架设计)

    Scrum 2.5 多鱼点餐系统开发进度  (下单一览页面-菜式添加框架设计) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题 ...

  2. 60个响应式的Web设计教程–能够手机访问!

    想要学习响应式[responsive:屏幕自适应的效果]的网页设计和开发技术?在这个超大的收藏集合中,我想你定会找到想要开始学习的响应式网页设计教程. 面对超过1亿的手机互联网用户,开发专业和用户友好 ...

  3. 微信小程序 - 非入侵式布局

    非入侵式布局,就是不影响原有内容以及代码,增加用户体验感(UE)的一种方式. 例如我们每个接口必须返回: 0:请求成功 -1:请求失败 .... 这样就便于前端判断数据是否加载成功,然后以客观的方式提 ...

  4. angular学习笔记(六)-非入侵式javascript

    这篇主要讲解非入侵式javascript. 在传统的前端开发中,把js写在html中,称为入侵式的javascript: <span id="select_area" onc ...

  5. 响应式web网站设计制作方法

    在研究响应式的时候,记录了一些感想,分享出来,抛砖引玉,希望可以和大家一起讨论.总结下来,响应式比之前想象的要复杂得多.1. ie9以下(不包括ie9)采用ie条件注释,为ie8以及一下单独开一个样式 ...

  6. 响应式web布局设计实战总结教程

    响应性web设计的理念是:页面的设计与开发应当根据用户行为与设备环境(包括系统平台,屏幕尺寸,屏幕定向等)进行相应的响应及调整.具体的实践方式由多方面组成,包括弹性网格和布局,图片,css Media ...

  7. 通用双向链表的设计(参考Linux系统中的实现)

    通常我们设计设计链表都是将数据域放在里面,这样每次需要使用链表的时候都需要实现一个链表,然后重新实现它的相关操作,这里参考Linux系统中的设计实现了一个通用的双向链表,只需要在你的结构里面有一个这个 ...

  8. Javascript 链式运动框架——逐行分析代码,让你轻松了解运动的原理

    所谓链式运动,就是一环扣一环.我们的很多运动实际上来说指的就是分阶段的,第一个阶段动完,下个阶段开始动. 这个链式运动框架就是用来处理这些问题的. 我们先来看下之前的运动框架,以下是Javascrip ...

  9. 【C/C++】关于隐式转换·面试题分析

    题目 以下两个程序片段A 和B ,问哪个能进入循环? 片段A: unsigned short i; unsigned ; ; i < index-; i++) { ........ } 片段B: ...

  10. 又一起.NET程序挂死, 用 Windbg 抽丝剥茧式的真实案例分析

    一:背景 1. 讲故事 前天有位粉丝朋友在后台留言让我帮忙看看他的 Winform程序 UI无响应 + 410线程 到底是啥情况,如下图: 说实话,能看到这些真实案例我是特别喜欢的 ,就像医生看病,光 ...

随机推荐

  1. Java 运算符 - 除法

    1. 除法运算符 Java中的除法运算符是"/"符号,表示将左侧操作数除以右侧操作数. 2. 整数除法 在Java中,整数除法的结果是一个整数,即只保留除法的整数部分,舍去小数部分 ...

  2. ASP.NET Core分布式项目实战(业务介绍,架构设计,oAuth2,IdentityServer4)--学习笔记

    任务4:第一章计划与目录 敏捷产品开发流程 原型预览与业务介绍 整体架构设计 API 接口设计 / swagger Identity Server 4 搭建登录 账号 API 实现 配置中心 任务5: ...

  3. C#开源免费的Windows右键菜单管理工具

    前言 今天分享一个C#开源.免费.纯粹的Windows右键菜单管理工具:ContextMenuManager. 工具主要功能 程序支持国际化多语言显示. 启用或禁用文件.文件夹.新建.发送到.打开方式 ...

  4. JS Leetcode 74. 搜索二维矩阵题解分析,二分法与坐标轴法

    壹 ❀ 引 本题来自Leetcode74. 搜索二维矩阵,虽然难度是中等,但如果站在做出来的角度,你会发现其实并不难,题目描述如下: 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值. ...

  5. [WPF] 使用Silk.NET绘制D3D9或OpenGL内容并完美解决空域问题。

    可扩展渲染控件实现的基本思路(D3D.OpenGL绘制所使用的基类): 首先创建一个抽象类 FramebufferBase,该类主要记录当前控件宽高和图像资源. public abstract cla ...

  6. ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR详解

    堆栈的实现方法    在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为'压栈'(push ).通常用一个指针(堆栈指针 SP-StackPointer ...

  7. PCIE详解

    老男孩读PCIe之一:从PCIe速度说起 从今天开始,老男孩要开始讲PCIe了.对我来说,这是个很大的挑战:首先,我自己本身,对PCIe并没有做到胸有成竹,我的PCIe知识也只是停留在理论阶段,我并没 ...

  8. java 从零开始手写 redis(十)缓存淘汰算法 LFU 最少使用频次

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...

  9. windows 程序启动后挂到后台

    开发中遇到一个问题,程序启动后,并没有出现在前台,而是自动挂到后台,程序处于卡死状态,基本任何模块都没加载进来. 后面排查发现跟一个功能的第三方 dll 有关系,在那个 dll 加载时导致程序卡死,因 ...

  10. queryset高级用法:select_related

    在提取某个模型的数据的同时,也提前将相关联的数据提取出来.比如提取文章数据,可以使用select_related将author信息提取出来,以后再次使用article.author的时候就不需要再次去 ...