C-数据结构-双向环链-变长结构体的使用-面向对象的思想

/*
通用性比较强的双向环链
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 指针设置为指向新节点 newnode
		newnode->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	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);
}

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 0
	char *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");
	else
		printf_s(data);
#endif
	llist_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); 这是一个指向函数的指针。为了更好地理解,我们将其分解并详细解释:

解释

  1. 函数指针

    • int (*insert)(LLIST *, const void *, int); 声明了一个指向函数的指针 insert
    • insert 指向的函数返回 int 类型,并且接受三个参数。
  2. 参数

    • 第一个参数是一个指向 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);

用途

函数指针非常有用,尤其在以下情况下:

  1. 回调函数:你可以将函数指针作为参数传递给另一个函数,以便在特定事件或条件发生时调用该函数。
  2. 动态函数调用:可以在运行时决定调用哪个函数,而不是在编译时确定。
  3. 实现多态性:在面向对象编程中,函数指针可以用于实现类似于C++中的虚函数的行为。

总结

int (*insert)(LLIST *, const void *, int); 是一个函数指针,指向一个接受 LLIST *const void *int 参数并返回 int 的函数。通过这种声明方式,可以灵活地在程序中使用函数指针,以实现动态函数调用和回调机制。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值