数据结构-有头双向循环链表2(封装)
list.c
/* *把数据结构封装 * 支持变长结构体:在网络传输的过程,包的大小不固定的情况适用 *实现create , delete ,insert , find , fetch等功能 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "list.h" //双向循环链表普通节点 struct llist_node_st { struct llist_node_st * prev ;//前驱 struct llist_node_st * next ;//后继 char data[1] ; }; //定义头节点LLIST类型,链表的起始地址 struct llist_head_st { int size ; struct llist_node_st head ; }; /* **************************** * 功能:创建带头节点链表 * 参数:initsize(变参的长度) * 返回:无 * ***************************/ struct llist_head_st * llist_create(int initsize) { //1.创建 struct llist_head_st * new ; new = malloc(sizeof(*new)); if(new == NULL) return NULL; //2.初始化 new->size = initsize ; new->head.prev = &new->head ; new->head.next = &new->head ; return new ; } /* ******************************************************* * 功能:以mode的方式向链表ptr中插入数据data * 参数:ptr:链表 * data:要插入的数据 * mode:插入的方式:FORWARD(头插) BACKWARD(尾插) * 返回:-1:内存申请失败,-2:方式不对,0:返回正确 * *******************************************************/ int llist_insert(LLIST *p , const void * data , int mode ) { //1.创建新节点并分配空间 struct llist_node_st *newnode ; struct llist_head_st *ptr = p; newnode = malloc(sizeof(*newnode) + ptr->size );//申请空间就是结构体大小+头节点里的size元素 if(newnode == NULL) return -1 ; //2.将data内容复制到新节点的数据中 memcpy(newnode->data,data,ptr->size); //3.判断插入方式并插入 if(mode == LLIST_FORWARD) { newnode->prev = &ptr->head ; newnode->next = ptr->head.next ; } else if(mode == LLIST_BACKWARD) { newnode->next = &ptr->head ; newnode->prev = ptr->head.prev ; } else return -3 ; printf("insert\n"); newnode->prev->next = newnode ; newnode->next->prev = newnode ; return 0 ; } /* ******************************************************* * 功能:以op的方式遍历链表ptr * 参数:ptr:链表 * op:遍历方式(main.c中使用print_s打印的方式遍历) * 返回:无 * *******************************************************/ void llist_travel(LLIST *p ,llist_op *op ) { //1.创建一个节点 struct llist_node_st *cur ; struct llist_head_st *ptr = p; //2.cur节点从头的后继开始;以不循环到头节点为止 ;向后移动 for (cur = ptr->head.next ; cur != &ptr->head ;cur = cur->next) { //3.将每个节点的data返回给用户 op(cur->data) ; } } /* ****************** * 功能:销毁链表ptr * 参数:ptr:链表 * 返回:无 * *****************/ void llist_destroy(LLIST *p) { //1.创建当前节点以及当前节点的下一个节点 struct llist_node_st *cur ; struct llist_node_st *next ; struct llist_head_st *ptr = p; //2.当前节点从头的后继开始;并且不等于头节点地址 ;指向下一个节点 for(cur = ptr->head.next ; cur != &ptr->head ; cur = next) { next = cur->next; //3.释放空间 free(cur); } //4.释放头节点 free(ptr); } /* ******************************************************* * 功能:从链表ptr,按照cmp的比较方式,查找key 只能内部调用,非接口函数 * 参数:ptr:链表 * key:查找内容 * cmp:比较函数 * 返回:返回find符合的节点指针,没有符合返回NULL * *******************************************************/ static struct llist_node_st * find_(struct llist_head_st *ptr ,const void *key ,llist_cmp *cmp) { //1.创建节点 struct llist_node_st *cur ; //2.当前节点从头的后继开始;并且不等于头节点地址 ;指向下一个节点 for(cur = ptr->head.next ;cur != &ptr->head ; cur = cur->next) { //3.如果匹配跳出循环,并返回当前节点的指针 if(cmp(key , cur->data) == 0 ) break; } return cur ; } /* ******************************************************* * 功能:从链表ptr,按照cmp的比较方式,查找key * 参数:ptr:链表 * key:查找内容 * cmp:比较函数 * 返回:返回相应节点的数据起始地址,否则返回NULL * *******************************************************/ void * llist_find(LLIST *p,const void *key ,llist_cmp *cmp) { //1.创建节点 struct llist_node_st *node ; struct llist_head_st *ptr= p; //2.调用find_函数得到匹配的节点 node = find_(ptr , key ,cmp); //3.没有找到匹配节点,返回空NULL if(node == &ptr->head)//头节点--没有找到 return NULL; //4.找到节点返回节点的数据的起始地址 return node->data; } /* ******************************************************* * 功能:从链表ptr,按照cmp的比较方式,删除key的节点 * 参数:ptr:链表 * key:查找内容 * cmp:比较函数 * 返回:成功返回0,否则返回-1 * *******************************************************/ int llist_delete(LLIST *p, const void *key,llist_cmp *cmp) { //1.创建节点 struct llist_node_st *node ; struct llist_head_st *ptr = p ; //2.找到匹配的节点,没找到返回-1 node = find_(ptr, key ,cmp ); if(node == &ptr->head) return -1 ; //3.更改前驱节点的后继、后继节点的前驱 node->prev->next = node->next ; node->next->prev = node->prev ; //4.释放节点,正确返回0 free(node) ; return 0 ; } /* ******************************************************* * 功能:从链表ptr,按照cmp的比较方式,删除key,并将删除节点回填data * 参数:ptr:链表 * key:查找内容 * cmp:比较函数 * 返回:返回相应节点的数据起始地址,否则返回NULL * *******************************************************/ int llist_fetch(LLIST *p , const void *key ,llist_cmp *cmp , void *data) { //1.创建节点 struct llist_node_st *node ; struct llist_head_st *ptr = p; //2.找到匹配的节点,没有返回-1 node = find_(ptr , key ,cmp); if(node == &ptr->head) return -1 ; //3.修改前驱节点的后继、修改后继节点的前驱 node->prev->next = node->next ; node->next->prev = node->prev ; //4.将要删除的节点回填 if(data != NULL) { memcpy(data , node->data , ptr->size); } //5.释放节点 free(node); return 0 ; }
list.h
#ifndef LIST_H__ #define LIST_H__ //插入模式 #define LLIST_FORWARD 1 #define LLIST_BACKWARD 2 typedef void LLIST ; //回调函数 typedef void llist_op(const void *) ; typedef int llist_cmp(const void *,const void *); void * llist_find(LLIST *ptr ,const void *key ,llist_cmp *); int llist_delete(LLIST *ptr,const void *key , llist_cmp * ); void llist_travel(LLIST *ptr ,llist_op *op ); int llist_fetch(LLIST *ptr , const void *key ,llist_cmp *cmp , void *data); void llist_destroy(LLIST *); #endif
main.c
#include <stdlib.h> #include <stdio.h> #include "list.h" #define NAMESIZE 32 //定义数据结构体 struct score_st { int id ; char name[NAMESIZE] ; int math ; int chinese ; }; //打印函数 static void print_s(const void *record) { const struct score_st *r = record ; printf("%d %s %d %d \n",r->id , r->name , r->math , r->chinese); } //id比较函数 static int id_cmp(const void * key ,const void *record) { const int * k = key ; const struct score_st *r = record ; return (*k -r->id); } //name比较函数 static int name_cmp(const void *key ,const void *record) { const char * k = key ; const struct score_st *r = record ; return(strcmp(k,r->name)); } int main() { //1.定义节点 LLIST *handler; //2.定义数据结构 struct score_st tmp ; //3.定义接收的数据结构指针 struct score_st *data = NULL; //4.变量i以及返回值ret int i ,ret ; //5.查找id 以及name int id = 3 ; char *del_name = "std6"; /************************************/ //1.创建节点:成功返回节点指针,失败返回NULL;参数为初始数据长度initsize handler = llist_create (sizeof(struct score_st)); if(handler == NULL) exit(1); printf("%d",__LINE__); //2.链表数据赋值:使用insert方式 for(i = 0 ; i < 7 ; i++) { tmp.id = i ; snprintf(tmp.name,NAMESIZE ,"std%d",i); tmp.math = rand()%100; tmp.chinese = rand()%100 ; ret = llist_insert(handler,&tmp,2); if(ret) exit(1); } //3.链表遍历 llist_travel(handler,print_s); printf("\n\n"); //4.链表数据查找 data = llist_find(handler , &id ,id_cmp); if(data == NULL) printf("can not find \n"); else print_s(data); printf("\n\n"); //5.链表数据删除 ret = llist_delete(handler ,del_name ,name_cmp); if(ret) printf("delete failed \n"); //6.链表遍历 llist_travel(handler,print_s); //7.链表销毁 llist_destroy(handler); exit(0); }
Makefie
all :main main:list.o main.o $(CC) $^ -o $@ clean: rm *.o main -rf