通用链表
通用链表作用
在链表相关知识(初学)中,写出来一个简单的链表基本操作,但是该链表有一个明显的弊端,即我们所实现的函数只能为person这个结构体服务,每个函数的入口参数都是person结构体,对应的原理和代码如下所示:
struct person{
char *name;
int age;
struct person *next;
};//定义一个结构体成员
struct list{
char *name;
struct person head;
};//定义一个链表
因此我们引入一个新的结构体node_t,使之能够形成一个新的链接形式,如下所示,在下面这个架构中,我们不管结构体是啥样的,只要是成员中包含node_t node的节点即可进行链接起来。
struct node_t{
struct node_t *next;
};
struct person{
char *name;
int age;
struct node_t node;
};//定义一个结构体成员
struct list{
char *name;
struct node_t head;
};//定义一个链表
通用链表的三种实现方式
方法一
把node_t结构体放在person结构体的最前面,使用时就可以直接获取到节点的位置,这种方案一般不使用。
使用此方案时,上述代码中的接引用可直接进行更改为下面这个简单形式。
p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = (struct person *)node;
方法二
方法二就是上述完整代码所用到的方法,通过node_t反推出person结构体的首地址。常用于linux操作系统中。
该方法我们通过用如下代码来代替上面的解引用代码,其中&((struct person *)0)->node)是为了得到基地址到node所占用的空间大小,(unsigned int)将其转化为整形的字节大小,(char *)node是为了将node变为字节类型的指针地址大小,减去后面的值即可得到对应的结构体基地址的地址大小,但是此时是字节类型的,因此最前面加上(struct person *)将其转化为结构体类型。
#define container_of(ptr,type,member) \
(type *)((char *)ptr - (unsigned int)&((type *)0)->member)
p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = container_of(node,struct person,node);
方法三
将node_t结构体加一个成员变量,直接指向对应节点的首地址,常用于单片机实时操作系统中。
方法三的使用方法如下
p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = node->container;
参考链接
此文章参考视频完成,具体链接为韦东山freeRTOS系列教程:FreeRTOS的内部机制
通用链表代码展示
#include "stdio.h"
struct node_t{
struct node_t *next;
};
struct person{
char *name;
int age;
struct node_t node;
};//定义一个结构体成员
struct list{
char *name;
struct node_t head;
};//定义一个链表
void InitList(struct list * plist,char *name)
{
plist->name = name;
plist->head.next = NULL;
}//初始化链表
void AddItemToList(struct list *plist, struct node_t *new_node)
{
struct node_t *last = &plist->head;
while (last->next){
last = last->next;
}
last->next = new_node;
new_node->next = NULL;
}//增加一个结构体成员,只能加在链表的最后面
void AddItemAfter(struct node_t *pre,struct node_t *new_node)
{
new_node->next = pre->next;
pre->next = new_node;
}//加入结构体成员,加在中间的某个位置,前提需要已知前一个节点
void DelItemFromList(struct list *plist, struct node_t *node)
{
struct node_t *per = &plist->head;
while (per != NULL && per->next != node){
per = per->next;
}
if(per == NULL)
return;
if(per->next == node)
per->next = node->next;
}//删除链表中的某个结构体成员,需要传入结构体成员地址
int CmpPersonAge(struct node_t *pre,struct node_t *next)
{
struct person *p;
struct person *n;
p = (struct person *)((char *)pre - (unsigned int)&((struct person *)0)->node);
n = (struct person *)((char *)next - (unsigned int)&((struct person *)0)->node);
if(p->age < n->age)
return -1;
else
return 0;
}
void SortList(struct list *plist)
{
struct node_t *pre1 = &plist->head;
struct node_t *pre2;
struct node_t *cur = plist->head.next;
struct node_t *next;
struct node_t *temp;
while (cur){
pre2 = cur;
next = cur->next;
while(next){
if(CmpPersonAge(cur,next) == 0){
//删除节点cur
DelItemFromList(plist,cur);
//删除节点next
DelItemFromList(plist,next);
//在pre1后面插入next
AddItemAfter(pre1,next);
//在pre2后面插入cur
if(pre2 == cur) //如果出现连续两个节点交换的情况
AddItemAfter(next,cur);
else
AddItemAfter(pre2,cur);
//指针互换
temp = cur;
cur = next;
next = temp;
}
pre2 = next;
next = next->next;
}
pre1 = cur;
cur = cur->next;
}
}//对链表进行排序(快速排序法)
void PrintList(struct list *plist)
{
int i = 0;
struct node_t *node = plist->head.next;
struct person *p;
while (node != NULL)
{
p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
printf("person %d : %s is %d %x\r\n",i++,p->name,p->age,p->node.next);
node = node->next;
}
}//对链表进行打印
int main()
{
printf("List_test!!!\r\n");
struct list list_A;//定义链表
struct person p[]={
{"p0",23,{NULL}},
{"p1",12,{NULL}},
{"p2",43,{NULL}},
{"p3",54,{NULL}},
{"p4",3,{NULL}},
{"p5",76,{NULL}},
{"p6",32,{NULL}},
{"p7",87,{NULL}},
{"p8",45,{NULL}},
{"p9",76,{NULL}},
{NULL,0,{NULL}}
};//定义结构体成员信息
InitList(&list_A,"class_A");//初始化链表
int i = 0;
while(p[i].name != NULL){
AddItemToList(&list_A,&p[i].node);
i++;
}//将结构体成员加载到链表中
PrintList(&list_A);
printf("\r\n");
DelItemFromList(&list_A,&p[3].node);//删除链表成员4
PrintList(&list_A);
printf("\r\n");
SortList(&list_A);//链表排序
PrintList(&list_A);
printf("\r\n");
return 0;
}