数据结构 | 通用数据类型双向链表(C语言)

本文介绍了如何在C语言中实现通用数据类型的双向链表,包括使用void指针存储任意类型数据,保证代码严谨性和健壮性的策略,以及如何实现数据结构的高复用、低耦合。文中提供了代码示例并进行了简单的测试。
摘要由CSDN通过智能技术生成


前言

熟悉C++的STL(标准模板库)的人都知道list底层是一个双向链表,支持通用类型数据的存储,使用起来非常方便,但对于C语言开发者来说,并没有这么方便的工具,所以在这里记录一下自己实现的通用数据类型双向链表,提供了一些常见的API,并分享一些常见的代码规范。



设计思路

这里我们不便过多地讨论双向链表的实现过程,我们重点看如何解决下面几个问题

1. 如何解决通用类型的数据存储
2. 如何保证代码的严谨性,健壮性
3. 如何实现数据结构的高复用,低耦合


1.通过 void 指针解决通用数据类型存储

思路:当链表节点的Data域是 void * 类型时才能够接收任意类型数据的指针,在插入链表时传入数据的地址,用户在使用时自己转换成原来数据类型的指针再使用即可,所以双向链表可以存 int ,double,甚至是结构体,链表类型的数据,完美的解决了通用数据类型存储的问题,链表结构体定义如下。

/* 通用数据类型,双向链表节点 */
typedef struct DNode
{
   
	struct DNode* prior;	/* 链表节点的前驱 */
	void* data; 			/* 链表节点的data域 */
	struct DNode* next;     /* 链表节点的后继 */
}DNode;

2.如何保证代码的严谨性,健壮性

很多人写代码不讲究代码的严谨性,总是匆匆的完成了功能就接手下一个,造成的结果往往是后期要投入大量的时间进行维护,所以最好做到以下几点。

1.传入指针避免使用assert放错,一旦错误程序立马终止,不利于错误检查
2.malloc申请内存空间之后一定要检查,防止出现malloc失败导致的后果
3.程序结束之后需要进行内存释放,并用专业内存检查工具检查
4.接口调用失败返回错误码,以便查看错误原因
5.外部不需要调用的接口使用 static 声明,防止用户调用


2.如何实现数据结构的高复用,低耦合

链表内部不提供打印函数,只提供遍历函数DListForeach,根据传入的回调函数来操作节点。
采用传入回调函数的方式,处理不同数据类型节点操作,节点空间释放,节点大小比较,将其函数指针作为接口参数传入,在链表内部进行自定义的操作,这是一个完美的解决方案,不太清楚函数指针的同学可以下去补补课,详细实现见代码。

这里为了通用性和便捷性,模拟了list的做法,给链表接口传入链表指针而不是头节点,所以定义了链表结构体。

/* 表示整个链表 */
typedef struct DList
{
   
   DNode* head;
   int listlength;
}DList;


代码展示

GenDList.h

#ifndef _DLIST_H_
#define _DLIST_H_
#include <stdbool.h>

// 函数的返回码
typedef enum DListRet
{
   
	DLIST_RET_OK,	// 操作调用成功
	DLIST_RET_FAIL,	// 操作调用失败
	DLIST_IS_NULL	// 链表为空
}DListRet;


/* 通用数据类型,双向链表节点 */
typedef struct DNode
{
   
	struct DNode* prior;	/* 链表节点的前驱 */
	void* data; 			/* 链表节点的data域 */
	struct DNode* next;     /* 链表节点的后继 */
}DNode;

/* 链表节点操作回调函数 */
typedef void (*DListNodeOperation)(void* outdata,
		void* intputData);

/* 销毁链表节点data域回调函数 */
typedef void (*DataDestory)(void* pDList, void* DNode);

/* 节点数据查找回调 */
typedef int (*DListDataFind)(void* content,void* data);

/* 节点数据比较回调 */
typedef int (*DListDataCompare)(void* ctx, void* data);
/* 表示整个链表 */
typedef struct DList
{
   
	DNode* head;
	int listlength;
}DList;


DList* DListCreat();

int DListLength(DList* pDList);

static DNode* CreateDNode(DList* pDist, void* data);

static DNode* GetPosNode(DList* pDList, int index);

DListRet InsertPos(DList* pDList, int index, void* data);

DListRet InsertBack(DList* pDList, void* data);

DListRet InsertFront(DList* pDList, void* data);

DListRet DListForeach(DList* pDist, DListNodeOperation nodeOp,
	void* outdata);

DListRet DeletePos(DList* pDList, int index,DataDestory datafreefun);

bool IsEmpty(DList* pDList);

void DlistDestory(DList
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值