1.单向链表基本操作
单向链表的创建,插入,删除,销毁,逆置 |
链表的结构定义 |
typedef struct _PtList { int data; struct _PtList *pNext; }PtList; |
链表的创建(带头节点) |
// 键盘一直输入,直到输入-1为止 // 可以选择用二级指针传出结果 PtList *createList() { PtList *pHead = NULL; int data; PtList *pNew = NULL; PtList *pCur = NULL; pHead = (PtList *)malloc(sizeof(PtList)); if (pHead == NULL) { printf("malloc err.\n"); return NULL; } pHead->data = 0; pHead->pNext = NULL; pCur = pHead; do { printf("请输入你的数据: "); scanf("%d", &data); if (data == -1) break; pNew = (PtList *)malloc(sizeof(PtList)); pNew->data = data; pNew->pNext = NULL; pCur->pNext = pNew; pCur = pNew; } while (data != -1); return pHead; } |
往链表中插入元素 |
// 在值为x的元素之前插入y,若没找到则在末尾插入 int insertList(PtList *pHead, int x, int y) { PtList *pTmp = NULL; PtList *pPri = NULL; PtList *pNew = NULL; int ret = 0; if (pHead == NULL) { printf("List is empty.\n"); ret = -1; return ret; } pTmp = pHead->pNext; pPri = pHead; while (pTmp != NULL) { if (pTmp->data == x) break; pPri = pTmp; pTmp = pTmp->pNext; } pNew = (PtList *)malloc(sizeof(PtList)); pNew->data = y; pNew->pNext = pTmp; pPri->pNext = pNew; return ret; } |
从链表中删除元素 |
int deleteList(PtList *pHead, int x) { PtList *pTmp = NULL; PtList *pPri = NULL; int ret = 0; if (pHead == NULL) { printf("List is empty.\n"); ret = -1; return ret; } pTmp = pHead->pNext; pPri = pHead; while (pTmp != NULL) { if (pTmp->data == x) { pPri->pNext = pTmp->pNext; free(pTmp); break; } pPri = pTmp; pTmp = pTmp->pNext; } return ret; } |
链表中元素的打印 |
int displayList(PtList *pHead) { PtList *pTmp = NULL; int ret = 0; if (pHead == NULL || pHead->pNext == NULL) { printf("List is empty.\n"); ret = -1; return ret; } pTmp = pHead->pNext; while (pTmp != NULL) { printf("%d\n", pTmp->data); pTmp = pTmp->pNext; } return ret; } |
整个链表的销毁 |
// 释放之后将pHead置NULL int destoryList(PtList **pHead) { PtList *pTmp = NULL; PtList *pCur = *pHead; int ret = 0; if (pCur == NULL) { printf("List is empty, do not need to destory.\n"); ret = -1; return ret; } pTmp = pCur; while (pTmp != NULL) { pCur = pCur->pNext; free(pTmp); pTmp = pCur; } *pHead = NULL; return ret; } |
链表的逆置 |
int reverseList(PtList *pHead) { PtList *p = NULL; /* 前驱结点 */ PtList *q = NULL; /* 当前结点 */ PtList *t = NULL; /* 临时指向下一个结点 */ int ret = 0; // 判断链表是否有效 if (pHead == NULL) { ret = -1; printf("List is empty.\n"); return ret; } // 判断链表是否需要逆置 if (pHead->pNext == NULL || pHead->pNext->pNext == NULL) { printf("List did not need to resvers.\n"); return ret; } // 初始化p,q的值 p = pHead->pNext; q = pHead->pNext->pNext; while (q != NULL) { /* 这里也是有规律的(上一行赋值元素和下一行被赋值元素) */ t = q->pNext; q->pNext = p; p = q; q = t; } //处理最后一个元素(逆置前第一个业务结点) pHead->pNext->pNext = NULL; //处理头结点的指向 pHead->pNext = p; return ret; } int displayList(PtList *pHead) { PtList *pTmp = NULL; int ret = 0; if (pHead == NULL || pHead->pNext == NULL) { printf("List is empty.\n"); ret = -1; return ret; } pTmp = pHead->pNext; while (pTmp != NULL) { printf("%d\n", pTmp->data); pTmp = pTmp->pNext; } return ret; } |
逆置思路 |
测试程序主函数 |
int main(void) { PtList *pHead = NULL; int ret = 0; pHead = createList(); ret = displayList(pHead); ret = insertList(pHead, 3, 8); ret = displayList(pHead); ret = deleteList(pHead, 3); ret = displayList(pHead); ret = reverseList(pHead); ret = displayList(pHead); ret = destoryList(&pHead); printf("%x\n", pHead); system("pause"); return 0; } |
2.结构体高级
计算结构体中元素的偏移量 |
/* @TYPE 业务结构体的类型 @MEMBER 业务结构体中嵌套的链表结构体成员变量 */ #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) |
计算业务结构体元素的地址 |
/* @ptr 链表结构体指针变量 @TYPE 业务结构体的类型 @MEMBER 业务结构体中嵌套的链表结构体成员变量 */ #define container_of(ptr, type, member) (type *)((char *)ptr - offsetof(type, member)) |
贴上一个自己写的求位置的例子 |
#include <stdio.h> #include <stdlib.h> #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) (type *)((char *)ptr - offsetof(type, member)) typedef struct _linklist { struct linklist *next; }Linklist; typedef struct _student { char name[60]; // 64 double age; // 8 (float 4) Linklist list; }Student; int main(void) { Student stu; Linklist *list = &stu.list; printf("%d\n", offsetof(Student, list)); printf("%d\n", container_of(list, Student, list)); printf("%d\n", &stu); system("pause"); return 0; } |
解释图 |
注:Linux内核链表就是这样实现的,很好的实现了业务和数据的分离。 |
3.Wmblinklist动态库的调用
函数原型 |
#ifndef _MYMLINKLIST_H_ #define _MYMLINKLIST_H_ typedef void LinkList; typedef struct _tag_LinkListNode { struct _tag_LinkListNode* next; }LinkListNode; /* typedef struct _tag_LinkListNode LinkListNode; struct _tag_LinkListNode { LinkListNode* next; }; */ LinkList* LinkList_Create(); void LinkList_Destroy(LinkList* list); void LinkList_Clear(LinkList* list); int LinkList_Length(LinkList* list); int LinkList_Insert(LinkList* list, LinkListNode* node, int pos); LinkListNode* LinkList_Get(LinkList* list, int pos); LinkListNode* LinkList_Delete(LinkList* list, int pos); #endif |
测试函数 |
#pragma comment(lib, "wmblinklist") #include "Mylinklist.h" #include <stdio.h> #include <stdlib.h> typedef struct _Teacher { LinkListNode list; /* 可以选择把链表元素放到开始,可以偷懒不去计算偏移量 */ char name[20]; int age; }Teacher; int main(void) { LinkList *list = NULL; Teacher t1, t2, t3; LinkListNode *tmp = NULL; int i; t1.age = 10; t2.age = 20; t3.age = 30; list = LinkList_Create(); LinkList_Insert(list, (LinkListNode *)&t1, LinkList_Length(list)); LinkList_Insert(list, (LinkListNode *)&t2, LinkList_Length(list)); LinkList_Insert(list, (LinkListNode *)&t3, LinkList_Length(list)); // 显示 for (i = 0; i < LinkList_Length(list); i++) { tmp = LinkList_Get(list, i); printf("%d\n", ((Teacher *)tmp)->age); } // 删除 for (i = 0; i < LinkList_Length(list); i++) { LinkList_Delete(list, i); } // 销毁 LinkList_Destroy(list); system("pause"); return 0; } |