以库的方式实现的双向循环链表

以库的方式实现的双向循环链表

main.c

#include <stdio.h>
#include "list.h"

//==================================================================================

#define NAMESIZE 32

//用户自定义的数据结构
typedef struct score_st
{
	int id;
	char name[NAMESIZE];
	int math;
	int chinese;
}score;

//由用户书写的回调函数
static void print_s(const void *record)
{
	//void *不能进行取值操作,转换成用户的数据类型
	const score *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 score *r = record;
	return (*k - r->id);
}
static int name_cmp(const void *key, const void *record)
{
	const char *k = key;
	const score *r = record;
	return strcmp(k, r->name);
}
//==================================================================================

int main(void)
{
	LIST *handler = list_create(sizeof(score));
	if (handler == NULL)
		exit(1);
	score tmp; //用户自定义的数据类型
	for (int i = 0; i < 7; i++)
	{
		tmp.id = i;
		snprintf(tmp.name, NAMESIZE, "stu%d", i);
		tmp.math = rand() % 100;
		tmp.chinese = rand() % 100;
		int ret = list_insert(handler, &tmp, LIST_FORWARD);
		if (ret)
			exit(1);
	}
	list_show(handler, print_s);

	printf("delete id = 3\n");
	int id = 3;
	int ret = list_delete(handler, &id, id_cmp);
	if (ret)
		printf("list_delete id failed\n");
	list_show(handler, print_s);

	printf("delete name = stu6\n");
	const char *name = "stu6";
	ret = list_delete(handler, name, name_cmp);
	if (ret)
		printf("list_delete name failed\n");
	list_show(handler, print_s);

	printf("find id = 30\n");
	int id1 = 30;
	struct score *data;
	data = list_find(handler, &id1, id_cmp);
	if (data == NULL)
		printf("Can not find\n");
	else
		print_s(data);
	
	list_destory(&handler);
	//printf("%p",handler->head.next);  //链表成功销毁则报段错误

	return 0;
}

list.c

#include "list.h"

//删除和拿回的操作也要用到find操作,所以把find函数拆开,并设置为静态函数,即只在内部使用
static node *find_(LIST *ptr, const void *key, list_cmp *cmp)
{
	node *current;
	for (current = ptr->head.next; current != &ptr->head; current = current->next)
	{
		if (cmp(key, current->data) == 0) //使用由用户书写的回调函数,==0表示找到
			break;
	}
	return current; //如果没找到则current = &ptr->head,而头结点的数据域正好为为空current->data = NULL;
}

LIST *list_create(int initsize)
{
	LIST *new = malloc(sizeof(LIST));
	if (new == NULL)
		return NULL;
	new->size = initsize;
	new->head.data = NULL;
	new->head.pre = &new->head;
	new->head.next = &new->head;
	return new;
}

//不知道用户的数据类型,用void *,且用户不希望你改他的数据,用const
//mode确定插入方式
int list_insert(LIST *ptr, const void *data, int mode)
{
	node *newnode = malloc(sizeof(node));
	if (newnode == NULL)
		return -1;
	newnode->data = malloc(ptr->size);
	if (newnode->data == NULL)
		return -2;

	//不确定数据类型,使用memcpy函数拷贝过来
	memcpy(newnode->data, data, ptr->size);

	if (mode == LIST_FORWARD)
	{
		newnode->pre = &ptr->head;  //头部结点的前驱是头结点
		newnode->next = ptr->head.next;
	}
	else if (mode == LIST_BACKWARD)
	{
		newnode->pre = ptr->head.pre;   
		newnode->next = &ptr->head;    //尾部结点的后继是头结点
	}
	else
		return -3;

	//这两句是尾部插入和首部插入都要执行的
	newnode->pre->next = newnode;  //新结点的前驱的后继是自己
	newnode->next->pre = newnode;  //新结点的后继的前驱是自己

	return 0;
}

void list_show(LIST *ptr, list_op *op)
{
	node *current = ptr->head.next; //跳过没有数据的头结点
	while (current != &ptr->head)
	{
		op(current->data);
		current = current->next;
	}
}

int list_delete(LIST *ptr,const void * key,list_cmp * cmp)
{
	struct node_st * node = find_(ptr,key,cmp);
	if(node == &ptr->head)
		return -1;       //链表中没有要删除的
	node->pre->next = node->next;
	node->next->pre = node->pre;
	free(node->data);
	free(node);
	return 0;
}

int list_fetch(LIST *ptr,const void * key,list_cmp *cmp,void * data)
{
	struct node_st * node = find_(ptr,key,cmp);
	if(node == &ptr->head)
		return -1;
	node->pre->next = node->next;
	node->next->pre = node->pre;
	if(data != NULL)
		memcpy(data,node->data,ptr->size);  //把数据拷贝出来
	free(node->data);
	free(node);
	return 0;
}

void list_destory(LIST **ptr)
{
	node *current,*next;
	LIST *p = *ptr;
	for (current = p->head.next; current != &p->head; current = next)
	{
		next = current->next;
		free(current->data);
		free(current);
	}
	free(p);         //释放头结点
	*ptr = NULL;    //如果不使用二级指针传参而使用一级指针,则这里置为NULL没有意义,仍然会产生野指针
}

void *list_find(LIST *ptr, void *key, list_cmp * cmp)
{
	node *p = find_(ptr, key, cmp);
	if(p == &ptr->head)
		return NULL;       //人为给予空指针
	return find_(ptr,key,cmp)->data;
}

list.h

#ifndef _LIST_H_
#define _LIST_H_

#include <stdlib.h>
#include <string.h>

#define LIST_FORWARD 1     //首部插入
#define LIST_BACKWARD 2    //尾部插入

typedef void list_op(const void *);
typedef int list_cmp(const void *, const void *);

//每一个普通结点的数据结构
typedef struct node_st
{
	void *data;     //不知道用户指定的数据类型
	struct node_st *pre;
	struct node_st *next;
}node;

//头结点的数据结构
typedef struct 
{
	int size;         //存储的用户数据类型的大小
	node head;
}LIST;   //拎着这个头指针就行了

LIST *list_create(int initsize);  //创建链表,实现一个带头结点的空的双向链表

int list_insert(LIST *,const void *data,int mode);

//第二个参数:指向回调函数入口地址的指针
void list_show(LIST *, list_op *);

void *list_find(LIST *, void *key, list_cmp *);

int list_delete(LIST *, const void *key, list_cmp *);  

//fetch和delete不同,fetch是删除并拿回数据(保存在void * data中),而删除是直接丢弃(仅此一点区别)
int list_fetch(LIST *,const void * key,list_cmp *,void * data);

void list_destory(LIST **);

#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值