/*
通用性比较强的双向环链
1.变长结构体的使用
2.面向对象的思想
*/
llist.h
#ifndef LLIST_H__
#define LLSIT_H__
#define LLIST_FORWARD 1
#definr LLIST_BACKWARD 2
typedef void llist_op(const void *);//回调函数
typedef int llist_cmp(const void *,const void *);struct llist_node_st
{struct llist_node_st *prev;struct llist_node_st *next; char data[0];//变长结构体的使用(占位符) 必须放在结构体的下方
};
typedef struct llist_head
{int size;struct llist_node_st head;int (*insert)(struct llist_head *,const void *,int);//面向对象 把函数写入结构体之中 函数指针的实现 指针是指向某个函数的入口地址void *(*find)(struct llist_head *,const void *,llist_cmp *);int (*delete)(struct llist_head *,const void *,llist_cmp *);int (*fetch)(struct llist_head *,const void *,llist_cmp *,void *);void (*travel)(struct llist_head *,llist *op);
}LLSIT;LLIST *llist_create(int initsize);
void llist_destroy(LLIST *);#endif
llist.c
#include<stdio.h>
#include<stdlib.h>
#include"llist.h"
#include<string.h>int llist_insert(LLIST *,const void *data,int mode);
void *llist_find(LLIST *, const void *key, llist_cmp *);//数据类型不统一使用void 百搭
int llist_delete(LLIST *,const void *key,llist_cmp *);
int llist_fetch(LLIST *,const void *key,llist_cmp *,void *data);
void llist_travel(LLIST *,llist_op *);LLIST *llist_create(int initsize)//只包含一个头节点( 双向循环链表)
{LLIST *new;new = malloc(sizeof(*new));if(new == NULL)return NULL;new->size= initsize;new->head.prev = new->head;new->head.next = new->head;new->insert = llist_insert;// llist_insert 不加括号是指针赋值 new->delete = llist_delete;new->find = llist_find;new->fetch = llist_fetch;new->travel = llist_travel;return new;
}
int llist_insert(LLIST *ptr,const void *data,int mode)
{struct llist_node_st *newnode;newnode = malloc(sizeof(*newnode) + ptr->size);if(newnode == NULL)return -1;memcpy(newnode->data,data,ptr->size);if(mode == LLIST_FORWARD){newnode->prev = &ptr->head;newnode->next = ptr->head.next;newnode->prev->next = newnode;//头节点的 next 指针设置为指向新节点 newnodenewnode->next->prev = newnode;//原本在头节点之后的节点的 prev 指针设置为指向新节点 newnode。}else if(mode == LLIST_BACKWARD){newnode->prev = ptr->head.prev;newnode->next = &ptr->head;newnode->prev->next = newnode;newnode->next->prev = newnode;}else{return -3;}return 0;
}
static struct list_node_st * find_(LLIST *ptr, const void *key, llist_cmp *cmp)
{struct llist_node_st *cur;for(cur = ptr->head.next;cur!=ptr.head;cur=cur->next){if(cmp(key,cur->data) == 0)break;}return cur;
}void *llist_find(LLIST *ptr, const void *key, llist_cmp *cmp)
{ struct llist_node_st *node;node = find_(ptr,key,cmp);if(node = &ptr->head)return NULL;return node->data;
}
int llist_delete(LLIST *ptr,const void *key,llist_cmp *cmp)
{ struct llist_node_st *node;node = nodefind_(ptr,key,cmp);if(node == &ptr->head)return -1;node->prev->next = node->next;node->next->prev = node->prev;free(node);return 0;
}
int llist_fetch(LLIST *ptr,const void *key,llist_cmp *cmp,void *data)
{struct llist_node_st *node;node = nodefind_(ptr,key,cmp);if(node == &ptr->head)return -1;node->prev->next = node->next;node->next->prev = node->prev;if(data!=NULL)memcpy(data,node->data,ptr->size);free(node);return 0;
}
void llist_travel(LLIST *ptr,llist_op *op)//需要一个回调函数,需要用户给我传一个函数
{struct llist_node_st *cur;for(cur = ptr->head.next;cur!=&ptr->head;cur=cur->next)//为了封装成更通用的函数,不知道用户的结构类型,因此需要回调函数,且需要在 .h文件中使用 void 函数声明,且使用typedef重命名 看起来更好一些op(cur->data);//借用户之手,把他知道的数据类型打印了出来 具有通用性}
void llist_destroy(LLIST *ptr)
{struct llist node_st *cur,*next;for(cur= ptr->head.next;cur != &ptr->head;cur= next){next = cur->next;free(cur);}free(ptr);
}
main.c
#include<stdio.h>
#include<stdlib.h>
#include"llist.h"#define NAMESIZE 32struct 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);
}static int id_cmp(const void *key,const void *record)
{const int *k = key;const struct score_st *r = record;return (*k - r->id);}
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()
{int ret,i;int id =3;LLIST *handler;struct score_st tmp;handler = llist_create(sizeof(struct score_st));if(handler == NULL)exit(1);for(i =0;i<7;i++){ tm.id =i;snprintf(tmp.name,NAMESIZE,"std%d",i);tmp.math = rand()%100;tmp.chinese = rand%()100;ret = handler->insert(handler,&tmp,LLIST_FORWARD);if(ret)exit(1);}handler->travel(handler,print_s);#if 0char *del_name = "std6";ret = llist_delete(handler,&id,id_cmp);//ret = llist_delete(handler,del_name,name_cmp)//如何实现根据任何字段来删除if(ret)printf("llist_delete failed!\n");llist_travel(handler,print_s);printf("\n\n");struct score *data;data = llist_find(handler,&id,id_cmp);if(data == NULL)printf("Can not find!\n");elseprintf_s(data);
#endifllist_destroy(handler);exit(0);
}
Makefile
all:llist
llist:llist.o main.o$(CC) $^ -o $@
clean:rm llist *.o -rf
补充说明
int (*insert)(LLIST *, const void *, int);
这是一个指向函数的指针。为了更好地理解,我们将其分解并详细解释:
解释
-
函数指针:
int (*insert)(LLIST *, const void *, int);
声明了一个指向函数的指针insert
。insert
指向的函数返回int
类型,并且接受三个参数。
-
参数:
- 第一个参数是一个指向
LLIST
类型的指针。 - 第二个参数是一个指向
const void
类型的指针。 - 第三个参数是一个
int
类型的变量。
- 第一个参数是一个指向
更详细的分解
int (*insert)
:insert
是一个指向返回类型为int
的函数的指针。(LLIST *, const void *, int)
:这个函数接受三个参数:LLIST *
:指向LLIST
类型的指针。const void *
:指向常量void
类型的指针,表示这个指针指向的数据不会被修改。int
:一个整数类型。
示例
为了更好地理解,我们可以考虑一个示例,其中有一个函数与上述函数指针匹配:
假设有一个函数 insert_function
,其定义如下:
int insert_function(LLIST *list, const void *data, int position) {// 插入操作的具体实现return 0; // 假设返回 0 表示成功
}
你可以将 insert
函数指针指向 insert_function
:
int (*insert)(LLIST *, const void *, int) = insert_function;
之后,你可以通过 insert
函数指针调用 insert_function
:
LLIST *myList;
const void *data;
int position = 5;
int result = insert(myList, data, position);
用途
函数指针非常有用,尤其在以下情况下:
- 回调函数:你可以将函数指针作为参数传递给另一个函数,以便在特定事件或条件发生时调用该函数。
- 动态函数调用:可以在运行时决定调用哪个函数,而不是在编译时确定。
- 实现多态性:在面向对象编程中,函数指针可以用于实现类似于C++中的虚函数的行为。
总结
int (*insert)(LLIST *, const void *, int);
是一个函数指针,指向一个接受 LLIST *
、const void *
和 int
参数并返回 int
的函数。通过这种声明方式,可以灵活地在程序中使用函数指针,以实现动态函数调用和回调机制。