2.1实现简单基础的vector
1.设计API
我们参考下C++ <std> 库中的vector, vector中的api很多,所以我们把里面用的频率很高的函数实现;
1.1 new&delete
new: 应该可以初始化一个我们需要的结构体指针并申请一段内存;
delete: 不仅能够把new出来的内存完整\安全的销毁,而且可以把元素中可能出现的内存指针中申请的内存销魂;
1.2 常用的api
append: 在数组的最末尾添加一个元素;
at: 获取指定位置的元素;
getSize: 查看当前数组的长度;
resize: 重新扩大当前数组的长度;
remove: 删除指定位置的元素;
clear: 删除现有的所有的元素;
insert: 在指定位置插入指定元素;
1.3 *拓展:打印功能 & 排序功能
deleteSud: 提供释放子元素可能指向的内存的函数
show: 方便快速的显示出元素内容 例如Printf;
toString: 由于支持自定义结构体,因此需要自己编写toString函数,在show函数汇总调用;
sort: 提供一个函数能够将数组里面的元素按照某种规则排序;
compare: 提供一个准则,比较两个元素的大小,commpare(A,B)A<B return 1 else return 0;
2.原理分析/设计实现
2.0 代码膨胀实现
代码膨胀可是参考以下使用宏定义的方式实现代码膨胀;
#include <stdio.h> // 一个膨胀结构体的宏 #define V_TYPEDEF(XX,TypeName) typedef struct \ {\ XX* data;\ unsigned int size;\ unsigned intrealsize;\ } VCT_##TypeName; /* // V_TYPEDEF(int,int_t) 等效于 typedef struct { int* data; unsigned int size; unsigned int realsize; } VCT_int_t; */ V_TYPEDEF(int,int_t) void main(void) { VCT_int_t a; a.size = 0; a.realsize = 20; a.data = (int *) malloc(sizeof(int) *20); }
2.1 数据结构选择
vector的特性是高效的随机读写,因此需要使用连续的数组;所以需要存储数据的数组 *data;
需要一个字段来管理当前数据写入的位置;同时也需要一个字段来管理申请的内存块大小;
由于C语言不支持函数重载,如果未来大规模使用vector,将会出现很多类似的at、getSize
等函数,为了方便程序员找到对应的函数,我们可以在里面添加相对应的函数指针,方便快速找到
对应的操作函数;
因此结构内成员为:
//----------------vector 自定义结构体实现----------------// #define V_TYPEDEF(XX,TypeName) typedef struct __##TypeName##_t \ {\ XX* data;\ u32 size;\ u32 realsize;\ u8 (* reSize)(struct __##TypeName##_t * v,u32 newsize);\ u32 (* getSize)(struct __##TypeName##_t * v);\ XX* (* at) (struct __##TypeName##_t * v,u32 index);\ u8 (* append)(struct __##TypeName##_t * v,const XX d);\ void (* clear)(struct __##TypeName##_t * v);\ void (* removeLast)(struct __##TypeName##_t * v);\ void (* remove)(struct __##TypeName##_t * v,u32 starX,u32 lengh);\ void (* insert)(struct __##TypeName##_t * v,u32 index,const XX value);\ void (* sort)(struct __##TypeName##_t * v);\ void (* show)(struct __##TypeName##_t * v);\ void (* compare)(const XX * v1,const XX * v2);\ void (* toString)(const XX * value);\ } VCT_##TypeName;
2.2 new函数实现
new函数实现比较简单,申请内存后给定里面所有的数据一个初始值即可;同时我们要注意的是给上
述结构体中的函数指针附上初值;并在宏处添加函数指针的赋值入口;
/* * new函数 返回相对应的vector 指针 */ #define V_NEW(TypeName,COMPARE,TOSTR,DELSUB) \ VCT_##TypeName* VCT_newVCT_##TypeName()\ {\ VCT_##TypeName* ret = malloc(sizeof(VCT_##TypeName));\ if(ret == NULL) return NULL;\ ret->size = 0;\ ret->realsize = 0;\ ret->data = NULL;\ ret->reSize = VCT_resizeVCT_##TypeName;\ ret->getSize = VCT_sizeVCT_##TypeName;\ ret->at = VCT_atVCT_##TypeName;\ ret->append = VCT_appendVCT_##TypeName;\ ret->clear = VCT_clearVCT_##TypeName;\ ret->removeLast = VCT_removelastVCT_##TypeName;\ ret->remove = VCT_removeVCT_##TypeName;\ ret->insert = VCT_insertVCT_##TypeName;\ ret->sort = VCT_sortVCT_##TypeName;\ ret->show = VCT_showVCT_##TypeName;\ ret->compare = COMPARE;\ ret->toString = TOSTR;\ ret->deleteSub = DELSUB;\ return ret;\ }
2.3 delete函数实现
delete首先要释放元素中可能指向的内存,然后在释放数组占用的内存,最后才是释放自身占用的内存;
代码如下:
/* * delete函数,清理相关结构体使用的堆内存空间 */ #define V_DELETE(TypeName)\ void VCT_deleteVCT_##TypeName(VCT_##TypeName* v)\ {\ if(v == NULL) return ;\ if(v->data == NULL) return ;\ for(u32 i = 0;i< v->size && v->deleteSub != NULL;i++)\ v->deleteSub(v->data[i]);\ free(v->data);\ free(v);\ }
2.4 append实现
append的功能为自动在数组的最末尾添加一个指定元素;需要注意的是,数组的长度并不是一直都
够用的,如果碰到申请的数组长度用完的情况(size == realsize)那么就需要使用resize扩大容量;
因此我们的逻辑是先对传参进行合法性判断,然后判断数据长度,并进行相应操作,最后追加元素;
返回插入结果(成功、失败);
*函数名称前添加‘__’下划线是因为为了防止在代码提示的时候名字高度重复带来的编写不便;
如果这里删除,那么声明和在New中赋值也需要修改,下面的库函数同理;
实现代码如下:
/* * vector 实现 append 函数,需要 传参V不为空,自动追加在vector数组的末尾 */ #define V_APPEND(XX,TypeName) \ inline u8 __VCT_appendVCT_##TypeName(VCT_##TypeName* v,const XX d)\ {\ if(v!= NULL)\ {\ if(v->size >= v->realsize)\ if(v->reSize(v,v->realsize*2+1) == FALSE)\ return FALSE;\ v->data[v->size] = d;\ v->size++;\ return TRUE\ }\ return FALSE\ }
2.5 at实现
at需要先判断当前位置是否越界;当然我们也可以使用v->data[i] 这样来调用我们的子项,但是这样
容易发生数组越界和其他问题;
代码如下:
/* * vector 实现 at 函数,需要 传参V不为空且 index 小于size 从0开始计数 */ #define V_AT(XX,TypeName) \ inline XX* __VCT_atVCT_##TypeName(VCT_##TypeName* v,u32 index)\ {\ if(v!=NULL && index < v->size) return v->data+index;\ else return NULL;\ }
2.6 getSize实现
这个没什么好说的 跟v->size作用一致,在此基础之上添加了一个防范措施;
代码如下
/** * vector 实现 size 函数,获取当前有实际意义的大小为多少 * */ #define V_SIZE(TypeName) \ inline u32 __VCT_sizeVCT_##TypeName(VCT_##TypeName* v)\ {\ if(v!=NULL) return v->size;\ else return -1u;\ }
2.7 resize实现
resize需要注意几点;1.如果申请内存失败了怎么办;2.当原先的resize大小为0时如何变大;
代码如下:
/* * vector 实现resize函数,当新的size大于原来的size时,此函数生效,否则则不生效. */ #define V_RESIZE(XX,TypeName) \ u8 __VCT_resizeVCT_##TypeName(VCT_##TypeName* v,u32 newsize)\ {\ if(v->realsize < newsize && v->size < newsize )\ {\ XX* temp = malloc(sizeof(*v->data)*newsize);\ if(temp == NULL) return FALSE;\ memcpy(temp,v->data,sizeof(*v->data)*v->size);\ memset(temp+v->size,0,sizeof(*v->data)*(newsize-v->size));\ free(v->data);\ v->data = temp;\ v->realsize = newsize;\ return TRUE;\ }\ return FALSE;\ }
2.8 remove实现
remove函数需要移动指定位置的数据向前,同时判断指定的位置和remove的长度是否
合法,并且释放指定元素肯能指向的内存;
代码如下:
/* * vector 实现 remove 函数,需要 传参V不为空,将vector 中从指定位置开始删除 指定长度的数据,如果长度过大则自动判断删除后面所有, * 真正的删除,其末尾一定会追加 一个全为 0 的数据字节 * . */ #define V_REMOVE(TypeName) \ inline void __VCT_removeVCT_##TypeName(VCT_##TypeName* v,u32 starX,u32 lengh) \ {\ if(v!= NULL) \ {\ if(lengh == 0) return ;\ else if(v->size > starX) \ {\ lengh = (v->size >(starX+lengh))?(lengh):(v->size - starX); \ for(u32 i = starX;i<v->size;i++) \ {\ if(i+lengh < v->size)\ {\ if(v->deleteSub!= NULL)\ v->deleteSub(v->data[i]);\ memcpy(v->data+i,v->data+i+lengh,sizeof(*v->data)); \ }\ else \ {memset(v->data+i,0,sizeof(*v->data)); break;}\ }\ v->size-=lengh;\ }\ }\ }
2.9 clear实现
等同于调用v->remvoe(v, 0,size);
代码如下:
/* * vector 实现 clear 函数,需要 传参V不为空,清空vector中所有有效的数据,都初始化为 0,并将size重置, * 但是其真正占用的内存并没有被释放. */ #define V_CLEAR(TypeName) \ inline void __VCT_clearVCT_##TypeName(VCT_##TypeName* v)\ {\ if(v != NULL) \ {\ for(u32 i = 0;i<v->size && v->deleteSub!= NULL;i++)\ v->deleteSub(v->data[i]);\ memset(v->data,0,sizeof(*v->data)*v->realsize);\ v->size = 0;\ }\ }
2.10 insert实现
insert的思想有点不一样,insert的思想是先移动开一个空的位置,然后才会放入指定元素;
代码如下:
/* * vector 实现 insert 函数,需要 传参V不为空,将vector 中从指定位置开始插入 指定数据,如果指定位置溢出则自动添加到最后, * . */ #define V_INSERT(T,TypeName) \ inline void __VCT_insertVCT_##TypeName(VCT_##TypeName* v,u32 index,const T value) \ {\ if(v!= NULL) \ {\ if(index > v->size)\ index = v->size;\ if(v->size >= v->realsize)\ v->reSize(v,v->realsize*2+1);\ for(u32 i = v->size;i>index;i--)\ memcpy(v->data+i,v->data+i-1,sizeof(*v->data));\ memcpy(v->data+index,&value,sizeof(*v->data)); \ v->size++;\ }\ }
3.优化/编写注释
因为上述代码是我在之前就已经写好了的库,主要的文件就是.h文件中的这些宏定义;
后续系列我会将所有要写的代码和可能优化的点都在这里添加哦~~
最后这里项目的git网址:https://github.com/KimAlittleStar/cstd
不定期更新,请佛系关注~
下一版预告:
Vector中实现insertVector,实现replaceSubVector(想想看 字符串处理 replace(“A”,“a”);)
Vector中实现快速排序~!!
目录
1.引言
3.2 C语言_实现数据容器set(基础版)
4 C语言_实现简单基础的map