为了代码简介高效,可以方便的被多个链表连接起来,而且这个链表可以很方便的被各种不同类型数据域复用,我们实现单双链表时候(链表节点中不需要数据域),可以像下面这样子:
typedef struct List
{
struct List* next;
struct List* pre;
//注:这里面没有数据域
}List_t;
typedef struct Student
{
char name[10];
int age;
int high;
//该学生可能存在于多个链表中,比如在男生链表,又在班级链表
List_t node1;
List_t node2;
}Student_t;
/**
* @fn ListInsert
* @brief 链表插入新节点
* @param newNode 新节点
* @param posNode 插入位置节点
* @return 无
* @note 默认插入到posNode后面
*/
void ListInsert(List_t* newNode, List_t* posNode)
{
newNode->pre = posNode;
newNode->next = posNode->next;
posNode->next->pre = newNode;
newNode->next = newNode;
}
void main()
{
Student_t s1,s2;
//s1和s2连接成链表1
ListInsert(&(s1.node1), &(s2.node1));
//s1和s2连接成另一个链表2
ListInsert(&(s1.node2), &(s2.node2));
}
此时,当我们得到了s1.node1的地址 (设为p1) 时候,现在想拿到s1的首地址(s1.node1所在结构体),怎么办呢,这时候,过程如下:
1. 获得Student结构体首地址和它的成员node1的地址差,如下
p = &( (Student*)0 -> node1 )? ? //思想:假设现在Student结构体的首地址假设为0,此时成员node1的地址就是相对偏移啦
2. 拿s1.node1的地址减去得到的地址差即可
p1 - p 就得到了我们想要的Student结构体的首地址。这个就是list_enty宏定义的实现原理,完整实现如下:
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ((type *)((u8*)ptr - offsetof(type,member)))
#define offsetof(type, member) ((u32) &((type *)0)->member)
Linux系统内核中的链表就是这样子实现滴喔。
参考文章:
Linux内核链表及list_entry解析_Hugo的博客-CSDN博客_linux list_entry
函数指针及其定义和用法,C语言函数指针详解
|