目录
双向循环链表
1.定义
2.创建双向链表
3.头插法
4.遍历打印
5.尾插法
6.删除
7.销毁
内核链表
1.内核链表的结构体
2.初始化头结点:INIT_LIST_HEAD
3.头插法:list_add
4.尾插法list_add_tail
5.删除节点: list_del
6.根据当前成员的地址获得节点首地址 :list_entry
//存放数据类型
typedef int DataType;
//节点类型
typedef struct node{
struct node *pPre;//前一个节点指针
DataType Data;//数据
struct node *pNext;//后一个节点指针
}LinkNode;
LinkNode *CreateDouList(void){
LinkNode *pTmpNode = NULL;
pTmpNode = malloc(sizeof(LinkNode));
if(NULL == pTmpNode){
return NULL;
}
pTmpNode->pPre = pTmpNode->pNext =NULL;
return pTmpNode;
}
//头插法
int HeadInsertDouList(LinkNode *pHead,DataType TmpData){
LinkNode *pTmpNode = NULL;
pTmpNode = malloc(sizeof(LinkNode));
if(NULL == pTmpNode){
return -1;
}
pTmpNode->Data = TmpData;
pTmpNode->pNext = pHead->pNext;
pTmpNode->pPre = pHead;
pHead->pNext = pTmpNode;
if(pTmpNode->pNext != NULL){
pTmpNode->pNext->pPre = pTmpNode;
}
return 0;
}
int ShowDouList(LinkNode *pHead){
LinkNode *pTmpNode = NULL;
pTmpNode = pHead->pNext;
while (pTmpNode != NULL)
{
printf("%d ",pTmpNode->Data);
pTmpNode = pTmpNode->pNext;
}
printf("\n");
return 0;
}
int TailInsertDouList(LinkNode *pHead,DataType TmpData){
LinkNode *pTmpNode = NULL;
LinkNode *pNewNode = NULL;
pNewNode = malloc(sizeof(LinkNode));
if(NULL == pNewNode){
return 0;
}
pTmpNode = pHead;
while (pTmpNode->pNext != NULL)
{
pTmpNode = pTmpNode->pNext;
}
pNewNode->Data = TmpData;
pNewNode->pNext = NULL;
pNewNode->pPre = pTmpNode;
pTmpNode->pNext = pNewNode;
return 0;
}
int DeleteDouList(LinkNode *pHead,DataType TmpData){
LinkNode *pTmpNode = NULL;
LinkNode *p = NULL;
int cnt = 0;
pTmpNode = pHead->pNext;
while (pTmpNode != NULL)
{
if(pTmpNode->Data == TmpData){
p = pTmpNode->pNext;
pTmpNode->pPre->pNext = pTmpNode->pNext;
if(pTmpNode->pNext != NULL){
pTmpNode->pNext->pPre = pTmpNode->pPre;
}
free(pTmpNode);
cnt++;
pTmpNode = p;
}else{
pTmpNode = pTmpNode->pNext;
}
}
return cnt;
}
int DestroyDouList(LinkNode **ppHead)
{
LinkNode *pFreeNode = NULL;
LinkNode *pTmpNode = NULL;
pTmpNode = *ppHead;
pFreeNode = *ppHead;
while (pTmpNode != NULL)
{
pTmpNode = pTmpNode->pNext;
free(pFreeNode);
pFreeNode = pTmpNode;
}
*ppHead = NULL;
return 0;
}
1.一种链表结构能够操作多种类型的数据对象
2.节点包含数据变成数据包含节点
可以通过(struct list_head *)这个小结构获取到对应的大结构(包含了这个节点的数据Data)的地址,从而操作数据。
/* 链表节点类型 */
struct list_head {
struct list_head *next;
struct list_head *prev;
};
#define INIT_LIST_HEAD(head) do { \
(head)->next = (head)->prev = head; \
} while (0)
功能:让头结点的next和prev都指向自己
list_add (struct list_head *new, struct list_head *head)
{
new->prev = head;
new->next = head->next;
new->prev->next = new;
new->next->prev = new;
}
功能:将new这个节点添加到头结点之后的位置。
注意:1.始终要保持着head的pre指向最后一个节点,最后一个节点的next指向头节点(双向链表)。
2.new是(struct list_head *)类型的,也就是小结构,所以这个添加节点添加的是小结构,不是大结构
/* 尾插法 */
static inline void
list_add_tail (struct list_head *new, struct list_head *head)
{
new->next = head;
new->prev = head->prev;
new->prev->next = new;
new->next->prev = new;
}
功能:将new这个节点,添加到head这个节点之前的位置
注意事项同头插法
list_del (struct list_head *old)
{
old->prev->next = old->next;
old->next->prev = old->prev;
old->next = (void *)0xbabebabe;
old->prev = (void *)0xcafecafe;
}
功能:从链表中删除节点
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
存在的意义:无论node在大结构中的哪个位置,都可以找到大结构的首地址,不然只有小结构在大结构的开头,小结构的地址可以充当大结构的地址
例子:type:大结构(data_t),member:小结构(node),pst:当前成员的地址
typedef struct data{
struct list_head node;
int num;
}data_t;
(unsigned long)(&((type *)0)->member))):大结构中小结构的偏移量
((type *)((char *)(ptr):把当前成员地址(小结构)转变成大结构
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 根据当前成员的地址(小)获得节点首地址(大结构)
7.从前向后遍历每个数据:list_for_each_entry
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
练习:
使用内核链表存储学生信息并打印
#include "list.h"
#include
//节点类型
typedef struct student
{
char name[32];
struct list_head node; //链表节点
char sex;
int age;
int score;
}stu_t;
int main(void)
{
struct list_head head;
struct list_head *pTmpNode = NULL;
stu_t *pstu = NULL;
//初始化空白节点
INIT_LIST_HEAD(&head);
stu_t s1 = {"张三", {NULL, NULL}, 'm', 19, 100};
stu_t s2 = { "李四", {NULL, NULL},'m', 19, 80};
stu_t s3 = {"王二", {NULL, NULL}, 'f', 19, 80};
stu_t s4 = {"赵五", {NULL, NULL}, 'f', 16, 90};
stu_t s5 = {"马六", {NULL, NULL}, 'm', 14, 60};
list_add_tail(&s1.node, &head);
list_add_tail(&s2.node, &head);
list_add_tail(&s3.node, &head);
list_add_tail(&s4.node, &head);
list_add_tail(&s5.node, &head);
list_del(&s3.node);
#if 0
list_for_each(pTmpNode, &head)
{
pstu = list_entry(pTmpNode, stu_t, node);
printf("姓名:%s\n", pstu->name);
printf("性别:%c\n", pstu->sex);
printf("年龄:%d\n", pstu->age);
printf("成绩:%d\n", pstu->score);
}
#endif
list_for_each_entry(pstu, &head, node)
{
printf("姓名:%s\n", pstu->name);
printf("性别:%c\n", pstu->sex);
printf("年龄:%d\n", pstu->age);
printf("成绩:%d\n", pstu->score);
}
return 0;
}