基于c语言的变长数组

1、废话

每次在单片机的编程中,都会有一个很尴尬的问题:数组没办法变长。
也不是说完全没办法变长吧,但是维护会变得很麻烦。就比如这样:

struct array {
	uint32_t 	len;
	type* 		a;	
};

说实话,这个数组维护的开销很大, 每次都需要memcpy。
这时候就会有人说,那我直接定义一个大数组, 每次就不要拷贝了,而且还不用担心越界,像这样:

struct array {
	uint32_t 	len;
	type 		a[256];	
};

好吧,我图省事的时候也是这样写= =。
后来我学了c++,发现它比c语言方便很多,就是单片机的兼容性和占的内存有点太大了,有的时候想用一点平时用的库什么的,发现塞不进去(这边建议多花点钱换一个(笑))
c++ 里有一个vector 能很方便的使用数组,而且还很安全。我就在想能不能复现这个,功能不需要很多,只要数组能变长,而且能使用不同的类型就行了,然后就花了一个下午码了一段代码。

2、简介

如题,能自动变长的数组。支持所有指针类型的数据(因为指针可以强转,不是指针的话内存管理太麻烦,而且不能通用)

3、实现

首先设计一个结构体,因为要节省开销,所以设计的尽可能简单。

typedef struct __Array Array;

struct ArrayInterface {
	void					(*set_data)(Array* arr, void* data, uint32_t index);		/*< 存数据 */
	void*					(*get_data)(Array* arr, uint32_t index);					/*< 取数据 */
	uint32_t				(*get_len)(Array* arr);										/*< 获得数组当前长度 */
	void					(*set_len)(Array* arr, uint32_t len);						/*< 设置数组当前长度 */ 
	void					(*set_unit)(Array* arr, uint32_t unit);						/*< 设置每次增加的内存大小 */ 
};

struct __Array {
	void**					data;					/*< 指向数据指针的指针, 用于保存数据数组 */ 
	
	uint32_t				max_len;				/*< 数据的最大大小, 超过时自动扩容 */
	uint32_t				now_len;				/*< 当前数据的长度 */
	uint8_t					unit_len;				/*< 每次增加的内存大小 */
	
	struct ArrayInterface*	interface;				/*< 统一的方法接口 */ 
};

其中的struct ArrayInterface是存储各种方法的函数指针列表,struct __Array是数组的结构体。其中有一个指向数据的指针数组data和一个数组当前最大大小的成员max_len每当当前的数据长度超过它时,就进行自动扩容。根据需求可以更改unit_len的值来达到尽可能少的进行扩容的操作。
上述的struct ArrayInterface是我定义的一系列外部访问的接口,调用的方式如下:

/* 设置一个模板用于支持不同类型 */
/* 
	note: 我暂时没有什么简单的方法来实现模板编程, 所以为了保护指针的安全
		只能使用这种不成熟的方法。 
*/
#define ARRAY(type)										\
	type* getData_##type(Array* arr, uint32_t index)	\
	{													\
		return (type*)arr->interface->get_data(arr, index);	\
	}													\
	void setData_##type(Array* arr, type* data, uint32_t index)	\
	{																	\
		arr->interface->set_data(arr, data, index);						\
	}													\
	void setUnit_##type(Array* arr, uint8_t unit)		\
	{													\
		arr->interface->set_unit(arr, unit);			\
	}													\
	void setLen_##type(Array* arr, uint32_t len)		\
	{													\
		arr->interface->set_len(arr, len);				\
	}													\
	uint32_t getLen_##type(Array* arr)					\
	{													\
		return arr->interface->get_len(arr);			\
	}

用宏实现对应类型的函数的定义,然后再去调用对应类型的函数,以实现各种数据的通用性。(ps. 人话是加一个强转类型,说实话用不用都没差,就是有个强转类型就不会有取void指针中的东西这种操作了)
还有一些简单的通用方法定义

/*< 用用函数接口 >*/ 
void arrayInit(Array* arr);
Array* arrayCreate();
void arrayDelete(Array* arr);
void setMemoryFunc(void* (*malloc_fn)(size_t size), void (*free_fn)(void* ptr));

其中setMemoryFunc函数是用于设置内存管理函数的,没设置就默认用c库的malloc和free。
之后就是各种方法的实现了,大题的框架都搭好了,实现不是很难,看代码应该都能看懂,这里就不啰嗦了。

#include "objectArray.h"
#include <stdlib.h>

/* 可以设置不同的内存管理接口, 兼容其它系统 */ 
struct Memory {
	void*		(*malloc_fn)(size_t size);			/*< malloc */ 
	void		(*free_fn)(void* ptr);				/*< free */
};

/* 默认值为标准库的函数 */
struct Memory memory = {
	.malloc_fn 	= malloc, 
	.free_fn 	= free
};

/* 打字太麻烦了用宏来表示 */
#define malloc_fn(ptr)		memory.malloc_fn(ptr)
#define free_fn(ptr)		memory.free_fn(ptr)

/*< 外部不可直接调用的方法 >*/ 
void __setData(Array* arr, void* data, uint32_t index);
void* __getData(Array* arr, uint32_t index);
void __setLen(Array* arr, uint32_t len);
uint32_t __getLen(Array* arr);
void __setUnit(Array* arr, uint32_t unit);

/* 将可以访问的函数地址存放到接口中 */ 
struct ArrayInterface interface = {
	.set_data =	__setData,
	.get_data = __getData,
	.set_len  = __setLen,
	.get_len  = __getLen,
	.set_unit = __setUnit
};

/**
 * @brief 数组扩容函数 
 * 
 * @param arr 句柄 
 * @param unit 扩容单位
 * 
 * @note 本函数的单位使用需谨慎, 每次扩容都有O1的时间复杂度 
 **/
void increase(Array* arr, uint8_t unit)
{
	void** old_data = arr->data;
	uint32_t i;
	
	arr->max_len += unit;
	arr->data = malloc_fn(arr->max_len);
	
	/* 遍历数组, 把之前的数组拷贝, 并初始化之后的数组 */ 
	for(i = 0; i < arr->max_len; i ++) {
		if(i < arr->now_len) {
			arr->data[i] = old_data[i];
		}
		else {
			arr->data[i] = NULL;
		}
	}
	
	/* 删除之前的数组 */ 
	if(old_data == NULL) {
		free_fn(old_data);
		old_data = NULL;
	}
}

/**
 * @brief 数组自动减少容量 
 * 
 * @param arr 
 * @param unit 
 **/
void reduce(Array* arr, uint8_t unit)
{
	void** old_data = arr->data;
	uint32_t i;
	
	arr->max_len -= unit;
	arr->data = malloc_fn(arr->max_len);
	
	/* 遍历数组, 把之前的数组拷贝 */ 
	for(i = 0; i < arr->max_len; i ++) {
		if(i < arr->now_len) {
			arr->data[i] = old_data[i];
		}
	}
	
	/* 删除之前的数组 */ 
	if(old_data == NULL) {
		free_fn(old_data);
		old_data = NULL;
	}
}

/**
 * @brief set方法 
 * 
 * @param arr 
 * @param data 
 * @param index 
 **/
void __setData(Array* arr, void* data, uint32_t index)
{
	while((arr->now_len >= arr->max_len) || (index >= arr->max_len)) {
		increase(arr, arr->unit_len); 
	}
	if(arr->max_len > 2 * arr->unit_len) {
		while(arr->max_len - 2 * arr->unit_len > arr->now_len) {
			reduce(arr, arr->unit_len);
		}
	}
	arr->data[index] = data;
}

/**
 * @brief get方法 
 * 
 * @param arr 
 * @param index 
 * 
 * @return 
 **/
void* __getData(Array* arr, uint32_t index)
{
	if(index < arr->max_len) {
		return arr->data[index];
	}
	else {
		return NULL;
	}
}

/**
 * @brief set方法 
 * 
 * @param arr 
 * @param unit 
 **/
void __setUnit(Array* arr, uint32_t unit)
{
	arr->unit_len = unit;
}

/**
 * @brief set方法 
 * 
 * @param arr 
 * @param len 
 **/
void __setLen(Array* arr, uint32_t len)
{
	arr->now_len = len;
} 

/**
 * @brief get方法 
 * 
 * @param arr 
 * 
 * @return 
 **/
uint32_t __getLen(Array* arr)
{
	return arr->now_len;
}

/**
 * @brief 初始化数组对象 
 * 
 * @param arr 对象指针
 **/
void arrayInit(Array* arr)
{
	arr->now_len = 0;
	arr->max_len = 0;
	arr->data = NULL;
	arr->unit_len = 8;
	arr->interface = &interface; 
}

/**
 * @brief 在堆上创建数组对象并初始化 
 * @return 
 **/
Array* arrayCreate()
{
	Array* arr = (Array*)malloc_fn(sizeof(Array));
	arrayInit(arr);
	return arr;
}

/**
 * @brief 删除数组对象 
 * 
 * @param arr 对象指针
 * @note 需要注意,使用 arrayCreate 函数创建的对象必须使用本函数删除,否则会一直存在 
 **/
void arrayDelete(Array* arr)
{
	free_fn(arr->data);
	arr->data = NULL;
	free_fn(arr);
	arr = NULL;
}

小结

虽然花的时间很少,但是这个库我还是比较满意的,毕竟有的时候有用到,其中有一个问题没用解决,数组的增加可以实现自动增加,但是减少的实现过于简单,我之前的想法是遍历数组,然后寻找NULL的最后一位,然后自动删除之后的长度,最后觉得这样每次进行的操作就会变得很多就放弃了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值