侵入式链表学习

在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)

 

posted @ 2023-09-04 11:08  namezhyp  阅读(532)  评论(0编辑  收藏  举报