首页 > 其他分享 >侵入式链表学习

侵入式链表学习

时间:2023-09-04 11:11:58浏览次数:37  
标签:node 学习 list next 链表 ListObj 侵入 prev

在408和常见教材里面,以普通的尾指针点单链表为例, 一个链表节点包含数据部分和尾指针。首个节点称为头节点,不包含数据,它的尾指针指向下一个节点(首个节点设计成存数据的也行)。每个节点依次连接,直到最后一个节点,尾指针设为null,表示链表结束。如果设计一个节点内有首尾两个指针,那就可以让它们分别指向上一个和下一个节点,构成双向链表。

 

 

struct ListNode {
   int value; 
  ListNode* next;
} //包含尾指针的单链表

struct ListNode {
  ListNode* prev;
  ListNode* next;
  T value;
}//包含首尾指针的双向链表

对于这样传统的非侵入式链表而言,是链表节点中包含数据。此外,链表上的所有节点,存的数据肯定都是相同的。与之对应的是侵入式链表,在linux里面广泛应用

这个图画的也不是完全准确,next应该被包在大框内。

这个设计看起来似乎和之前的数据结构区别不大,事实上也确实区别不大,但是指针被包在了数据内:

typedef struct list_structure
{
    struct list_structure* next;
}ListObj;//指针节点单独处理
​
typedef struct
{
    int data1;
    ListObj list;
} Node_int;  //只要包含指针节点就行

typedef struct
{
    float data2;
    ListObj list;
} Node_float;

指针节点单独写成结构体,这样其他节点可以按照自己的需求重新写结构体,只需要在里面包含指针结构体就行。这样一来,每个节点的数据都可以按需要来,而且所有链表的操作都可以统一。

linux下链表头文件:

#ifndef _LIST_H
#define _LIST_H
​
#define offset_of(type, member)             (unsigned long) &((type*)0)->member
​//计算结构体成员相对结构体的偏移
//使用了一个技巧,将一个空指针转换为结构体类型的指针,然后获取成员的地址
//并将其转换为 unsigned long 类型。这样就获得了结构体成员相对于结构体起始地址的偏移量。

#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offset_of(type,member) );}) ​//通过结构体和类型,结构体成员的地址来获取结构体的首地址 ​//它首先将传入的指针 ptr 强制转换为指向成员的指针,
//然后使用 offset_of 宏计算出成员相对于结构体的偏移量。
//最后,通过从成员地址减去偏移量的方式,得到结构体的首地址。

typedef struct list_structure { struct list_structure* next; struct list_structure* prev; }ListObj; ​ ​ #define LIST_HEAD_INIT(name) {&(name), &(name)} #define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name) ​ ​ #define list_entry(node, type, member) \ container_of(node, 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) ​ ​ void list_init(ListObj* list); void list_insert_after(ListObj* list, ListObj* node); void list_insert_before(ListObj* list, ListObj* node); void list_remove(ListObj* node); int list_isempty(const ListObj* list); unsigned int list_len(const ListObj* list); ​ #endif

源代码:

#include "link_list.h"
 
void list_init(ListObj* list)
{
    list->next = list->prev = list;
}
​
void list_insert_after(ListObj* list, ListObj* node)
{
    list->next->prev = node;
    node->next = list->next;
​
    list->next = node;
    node->prev = list;
}
​
void list_insert_before(ListObj* list, ListObj* node) 
{
    list->prev->next = node;
    node->prev = list->prev;
    list->prev = node;
    node->next = list;
}
​
void list_remove(ListObj* node)
{
    node->next->prev = node->prev;
    node->prev->next = node->next;
​
    node->next = node->prev = node;
}
​
int list_isempty(const ListObj* list)
{
    return list->next == list;
}
​
unsigned int list_len(const ListObj* list)
{
    unsigned int len = 0;
    const ListObj* p = list;
    while (p->next != list)
    {
        p = p->next;
        len++;
    }
    return len;
}

 

 

参考来源:  C语言侵入式链表 - 掘金 (juejin.cn)

 

标签:node,学习,list,next,链表,ListObj,侵入,prev
From: https://www.cnblogs.com/namezhyp/p/17676409.html

相关文章

  • 机器学习算法编程小技巧——numpy用法之np.c_
     importnumpyasnp#创建两个一维数组a=np.array([1,2,3])b=np.array([4,5,6])#使用numpy.c_将它们连接在一起"""numpy.c_是一个方便的工具,用于沿第二轴连接数组。它将数组转换为至少2-D,并将它们堆叠在一起。这在需要将多个数组组合成一个更大数组的情况......
  • Spring Boot(04):让你的Spring Boot应用“火力全开”,从零开始学习starter
    ......
  • 多线程学习笔记
     1.进程和线程进程是指一个程序,例如QQ,打开会占用一定的内存和空间,会有产生和消亡。线程是由进程创造,一个进程可以有多个线程。单线程:在同一个时刻,只允许执行一个线程。多线程:在同一个时刻,允许执行多个线程。并发:同一时刻,多个任务交替执行,例如一台电脑同时运行qq和迅雷,看着貌似是有......
  • 机器学习 -> Machine Learning (III)
    来做一些入门题吧.以下大多是kaggle环境.Q1Titanichttps://www.kaggle.com/competitions/titanicimport#ThisPython3environmentcomeswithmanyhelpfulanalyticslibrariesinstalled#Itisdefinedbythekaggle/pythonDockerimage:https://github.com/......
  • 《一般图最大匹配》学习总结
    带花树学不会,不玩了。咕掉。随机化来学随机化吧。。。实际上在随机数据上表现甚至优于带花树,不过他为什么要随机而且为什么随机就能搞我也不知道。就背一个板子就好了。点击查看代码#include<bits/stdc++.h>typedeflonglongLL;usingnamespacestd;constintMAXN=1......
  • 剑指 Offer 06. 从尾到头打印链表
    剑指Offer06.从尾到头打印链表方法一顺序添加,再翻转。classSolution{publicint[]reversePrint(ListNodehead){ListNodeh=head;List<Integer>res=newArrayList<>();while(h!=null){res.add(h.val);h=......
  • pyspark学习
    frompysparkimport*frompyspark.sqlimportSparkSessionfrompyspark.sqlimportfunctionsasfimportjsonimportosfrompyspark.sql.typesimportStructType,IntegerType,StringType#os.environ['HADOOP_CONF_DIR']='/export/server/h......
  • 链表
    链表当需要插入或删除数据结构中某些元素时,用链表比数组方便得多,访问某一元素时则链表就是个dd。(链表可以用来秀指针操作注意:链表中的每个元素在使用前都要申请一个空间(new)并指向NULL(->next=NULL),即初始化操作由于链表不能访问某一元素,所以每次操作前都要从头开始(p=h......
  • 单链表题目*4
    //获取单链表有效结点个数publicstaticintgetLength(ListNodehead){if(head.next==null){return0;}intresult=0;ListNodetemp=head.next;while(temp!=null){result++;temp=temp.next;}returnresult;}//......
  • 《Java编程思想第四版》学习笔记22
    注意下面这两句话:1、针对g()和main(),Throwable类必须在违例规格中出现,因为fillInStackTrace()会生成一个Throwable对象的句柄。由于Throwable是Exception的一个基础类,所以有可能获得一个能够“掷”出的对象(具有Throwable属性),但却并非一个Exception(违例)。因此,在main()......