2.1实现简单基础的vector
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 = ;
a.realsize = ;
a.data = (int *) malloc(sizeof(int) *);
}
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 = ;\
ret->realsize = ;\
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 = ;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*+) == 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,,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 == ) 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,,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 = ;i<v->size && v->deleteSub!= NULL;i++)\
v->deleteSub(v->data[i]);\
memset(v->data,,sizeof(*v->data)*v->realsize);\
v->size = ;\
}\
}
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*+);\
for(u32 i = v->size;i>index;i--)\
memcpy(v->data+i,v->data+i-,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
2.1实现简单基础的vector的更多相关文章
- java:Spring框架1(基本配置,简单基础代码模拟实现,spring注入(DI))
1.基本配置: 步骤一:新建项目并添加spring依赖的jar文件和commons-logging.xx.jar: 步骤二:编写实体类,DAO及其实现类,Service及其实现类; 步骤三:在src下 ...
- cocos2dx的模板容器简单使用(Vector,Map,Value)
在cocos2dxv3.0beta之前存在顺序性容器cocos2d::CCArray,和cocos2d::CCDictionary.可是在新版本号之后这两个容器都将被cocos2d::Vector&l ...
- java学习之路--简单基础的面试题
1.面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面.抽象只关注对象有哪些属性和行为,并不关注 ...
- C++学习3--编程基础(vector、string、三种传参)
知识点学习 Vector容器 vector是C++标准程序库中的一个类,其定义于头文件中,与其他STL组件一样,ventor属于STD名称空间: ventor是C++标准程序库里最基本的容器,设计之初 ...
- 你所要掌握的最简单基础的React渲染优化
一.React的渲染机制 要掌握一两项React-render优化的方法不难,但是非常重要.无论是在实际项目中的一个小细节,还是迎合'面试官'的口味 1.1 触发Render 我们知道React要更新 ...
- C/C++基础----string, vector, array
1 using声明 方便使用命名空间中的成员,不用每次xxx::yyy 头文件不应该包含using声明(不经意间包含了一些名字) 2 string 表3.1:初始化string对象的方式 string ...
- java 学习第一篇简单基础
Java基础 Java Java 和C#有着极为相似的语法. 和C#都是面向对象的高级程序语言. JAVA是一个开源,公开的语言,有着极其丰富的开源库和其他资源. JAVA分类 JAVA分SE EE ...
- ADB简单基础命令
1.查看设备 adb devices 这个命令是查看当前连接的设备, 连接到计算机的android设备或者模拟器将会列出显示 2.安装软件 adb install adb install :这个命令将 ...
- 黑马程序员——JAVA基础之Vector集合
------- android培训.java培训.期待与您交流! ---------- 基本已被淘汰 枚举就是Vector特有的取出方式.枚举和迭代器很像.其实枚举和迭代是一样的. 因为枚举的名称 ...
随机推荐
- Python集训营45天—Day02
目录 变量和运算符 1.1 初步介绍 1.2 使用案例 1.3 知识点梳理 1.4 练习 序言:这一章我们将学习变量以及常见的类型,我们将以案例和代码相结合的方式进行梳理,但是其中所有的案例和知识点 ...
- 一、springboot起航
# 前言 之前零零散散的学习了一些springboot的知识,以及搭建一些springboot的项目,甚至还有一些项目应用到实际项目中了,但是突然有一天想要建一个自己的项目网站.发现自己不知道从何开始 ...
- 手把手教程: CentOS 6.5 LVS + KeepAlived 搭建 负载均衡 高可用 集群
为了实现服务的高可用和可扩展,在网上找了几天的资料,现在终于配置完毕,现将心得公布处理,希望对和我一样刚入门的菜鸟能有一些帮助. 一.理论知识(原理) 我们不仅要知其然,而且要知其所以然,所以先给大家 ...
- MySQL单标查询
一 单表查询的语法 #查询数据的本质:mysql会到你本地的硬盘上找到对应的文件,然后打开文件,按照你的查询条件来找出你需要的数据.下面是完整的一个单表查询的语法 select * from,这个se ...
- 实现非管理型UPS在linux主机上的停电自动关机
买了个山特的SANTAK TG-BOX 850 UPS,自带USB通讯线缆.本以为官方软件提供Linux下的CLI命令以监控UPS状态. 官网提供的下载链接巨慢无比不说,CLI下只提供了安装脚本,没有 ...
- C++ std::thread概念介绍
C++ 11新标准中,正式的为该语言引入了多线程概念.新标准提供了一个线程库thread,通过创建一个thread对象来管理C++程序中的多线程. 本文简单聊一下C++多线程相关的一些概念及threa ...
- BCD 码、Gray 码、ASCII 码都是什么呢?
BCD 码:即(Binary Coded Decimal)码,也称为 8421 码,是十进制代码中最常见的一种.每一位的 1 代表的十进制数称为这一位的权.BCD 码中每一位的权都是固定不变的,它属于 ...
- 如何优雅的使用springboot项目内置tomcat
问题:以前,我们在使用SSM框架的时候,都是通过外置的tomcat进行部署,如果想访问文件,直接拖到项目的根目录下面即可.假如我们需要放一个apk文件,然后让别人下载,只需将apk放到项目根目录下面, ...
- jQuery九大选择器和jQuery对ajax的支持
一.jQuery九大选择器 1)基本选择器: <body> <div id="div1ID">div1</div> <div id=&qu ...
- 夯实Java基础系列17:一文搞懂Java多线程使用方式、实现原理以及常见面试题
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...