之前说过鸿蒙实现了自己的vector容器,叫做SimpleVector,其代码位于distributedschedule_samgr_lite\interfaces\kits\samgr\common.h,现在来分析下其代码。
1 typedef struct SimpleVector { 2 int16 max; //可存储的最大数据记录数。 3 int16 top; //存储的数据记录数量的峰值。 4 int16 free; //已释放的数据记录数。 5 void **data;//数据段指针 6 VECTOR_Key key;//函数指针,将数据元素转换为用于比较的键,默认值为NULL 7 //1 表示key1大于key2 8 //0 表示key1等于key2 9 //-1 表示key1小于key2 10 //默认值为NULL 11 VECTOR_Compare compare;//函数指针,指向相应的键值比较函数 12 } Vector;
宏定义
1 //对于T类型的对象,获取它的member成员相较于起始地址的偏移量 2 #define GET_OFFSIZE(T, member) (long)((char *)&(((T *)(0))->member)) 3 /* 4 1.首先获取member对于T的起始地址的偏移量 5 2.获取ptr-偏移量的地址值,然后转化为T类型 6 */ 7 @Ptr:成员变量的地址 8 @T:目标类型 9 @member:将@Ptr的名称指定为T成员变量 10 #define GET_OBJECT(Ptr, T, member) (T *)(((char *)(Ptr)) - GET_OFFSIZE(T, member))
这里的宏定义非常重要,在后续的代码中频繁的出现,有的小伙伴可能对于文字注释不太理解,所以我以下面一张图来进一步的说明。
首先拟定一个T类型的结构体,包含三个成员a,b和member。一般情况下,它的存储方式如图上半部分,a的地址即结构体的起始地址,并且与0地址间有不定长的距离。而GET_OFFSIZE
的作用就是获取member成员相较于结构体起始地址a的偏移量,这里用的巧妙的地方就是直接将0地址转换为T类型,然后member的地址值就是要求的偏移量,如图的下半部分,这样可以避免做减法操作。
而GET_OBJECT
的作用就显而易见了,即根据member在结构体中的偏移量来获取这个结构体的首地址。实际使用中ptr所指向的对象就是member类型的,所以通过ptr-偏移量就可以得到结构体的首地址。这样我们就可以推断出结构体的全貌。
针对common.h
中的函数声明,对它们相应的实现进行分析。实现在模块2-distributedschedule_samgr_lite\samgr\source\common.c
文件中。
1 /* 2 函数功能:创建vector对象 3 函数参数:@key:函数指针,指向将数据元素转换为键值的函数 4 @compare:函数指针,指向比较两个元素的函数 5 函数返回:返回一个vector对象 6 详细描述:初始化vector的成员,并给函数指针赋值 7 */ 8 Vector VECTOR_Make(VECTOR_Key key, VECTOR_Compare compare) 9 { 10 //初始化vector成员,并给函数指针赋值 11 Vector vector = {0, 0, 0, NULL, key, compare}; 12 return vector; 13 } 14 /* 15 函数功能:清空vector的内容 16 函数参数:@vector:vector对象 17 详细描述:释放vector的data指向的内存,并将vector对象的各个成员置为初始值。 18 */ 19 void VECTOR_Clear(Vector *vector) 20 { 21 //参数检查 22 if (vector == NULL) { 23 return; 24 } 25 if (vector->data == NULL) { 26 return; 27 } 28 //释放data指向的内存空间 29 SAMGR_Free(vector->data); 30 //标记值全部归0 31 vector->max = 0; 32 vector->top = 0; 33 vector->free = 0; 34 vector->data = NULL; 35 }
vector添加元素操作
1 /* 2 函数功能:在vector对象中添加新的元素 3 函数参数:@vector: vector对象 4 @element: 待添加的元素 5 函数返回:成功返回新加入的元素的下标,失败返回INVALID_INDEX 6 详细描述: 7 1.将元素添加到vector中,首先做参数检查。 8 2.判断vector中data是否有空闲空间,如果没有空闲空间,先遍历data中为NULL的元素, 9 若找到便将element加入后返回,没找到就申请新的空间。 10 3.最后将element加入data中,返回下标。 11 */ 12 int16 VECTOR_Add(Vector *vector, void *element) 13 { 14 //参数检查 15 if (vector == NULL || element == NULL) { 16 return INVALID_INDEX; 17 } 18 //无空闲空间 19 if (vector->top >= vector->max) { 20 int16 i; 21 //优先使用已释放的数据元素,从后往前遍历 22 for (i = vector->top - (int16)1; i >= 0; --i) { 23 if (vector->data[i] == NULL) { 24 //若找到一个指向==NULL的位置,则将元素插入 25 vector->data[i] = element;//data[i]指向element对应的内存空间 26 //更新已释放数,减一。代表重用了一块被释放的区域 27 vector->free--; 28 //返回插入的下标 29 return i; 30 } 31 } 32 //不存在可重用的位置,需申请新的空间 33 //max占用两个字节,判断增长是否会导致溢出 34 if (vector->max + GROW_STEP < 0) { 35 //容量达到上限,返回INVALID_INDEX 36 return INVALID_INDEX; 37 } 38 //重新申请新的空间,大小为当前容量max+增长步长GROW_STEP 39 void **data = (void **)SAMGR_Malloc(sizeof(void *) * (vector->max + GROW_STEP)); 40 if (data == NULL) { 41 //内存分配失败 42 return INVALID_INDEX; 43 } 44 //若data指向一块内存,则需要拷贝 45 if (vector->data != NULL) { 46 //将源data中的数据拷贝到新的data中 47 (void)memcpy_s(data, sizeof(void *) * (vector->max + GROW_STEP), 48 vector->data, sizeof(void *) * vector->max); 49 //释放旧的data 50 SAMGR_Free(vector->data); 51 } 52 vector->data = data;//更新数据段 53 vector->max += GROW_STEP;//更新最大容量 54 } 55 //有空闲空间,将element加入 56 vector->data[vector->top] = element; 57 return vector->top++;//先返回当前元素的下标,再自增 58 }
vector中的元素交换操作,当与NULL交换时可用于删除元素
1 /* 2 函数功能:交换指定下标的元素 3 函数参数:@vector: vector对象 4 @index: 指定索引 5 @element: 指定元素 6 函数返回:成功 返回被替换的元素,失败 返回NULL 7 详细描述: 8 1.函数调用传入三个参数,待操作的vector对象、元素索引下标、当前元素 9 2.首先进行参数检查,若检查失败就返回NULL 10 3.判断当前元素是否为NULL,若为NULL 则free+1. 11 4.当element指向NULL时,即当前替换操作变为删除指定元素的操作 12 5.在替换前先保存旧的元素指针,再替换,并返回旧元素指针。 13 */ 14 void *VECTOR_Swap(Vector *vector, int16 index, void *element) 15 { 16 //参数检查 17 if (vector == NULL || vector->top <= index || index < 0) { 18 return NULL; 19 } 20 //若当前元素为NULL则free数+1 21 if (element == NULL) { 22 vector->free++; 23 } 24 //保存当前索引中对应的旧元素 25 void *oldElement = vector->data[index]; 26 vector->data[index] = element;//交换 27 return oldElement; 28 }