当前位置:   article > 正文

SamgrLite——重要结构体Vector的相关知识_结构体中的vector

结构体中的vector

在完结SA服务框架初始化之前,我们先来补补课,看看这个频繁出现在代码中的结构体,弄清楚它们能让我们阅读源码时更加得心应手,也能够体会到鸿蒙开发人员设计函数和结构体时的巧妙构思,开拓眼界

1. void类型是什么?

这里先穿插一个非常重要的知识点——有利于后面讲解的展开
void关键字我们都知道是指定没有没有可用的值。在前面的文章中提到了void在鸿蒙OS中的一个妙用——通过在函数体中void(parameter)使得那么没有传入实参的形参不会导致编译warning

在common.c和其他函数中我们经常看到这样的写法:
在这里插入图片描述
在Vector的结构体中data的类型:
在这里插入图片描述
所以这里总结一下void的常见用法:

  1. 函数返回为空——常见的用法,函数不返回任何值比如void main
  2. 函数参数列表为空——不需要传入任何参数比如void publish(void)
  3. void的用法——void指针作为左值表示可用接受任意类型的指针;void*指针作为右值赋给其他类型的指针时需要进行强制转换

也就是是说void类型的指针能够指向任意类型的指针,一般用于数据封装和通用设计,而Vector能够管理不同类型比如serviceImpl或者featureImpl就是因为使用了void类型指针的缘故

2. Vector

主要的相关文件在common.h和common.c中

2.1 Vector结构体

首先在common.h中可以看到其详细的结构体:

typedef struct SimpleVector{
	int16 max;
	int16 top;
	int16 free;
	void **data;
	VECTOR_Key key;
	VECTOR_Compare compare;
}

typedef void *(*VECTOR_Key)(const void *);
typedef int (*VECTOR_Compare)(const void *, const void *);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • max:vector中data的最大存储范围规定即data[max]
  • top:当前存储在data中的最大地址,通常data中指针的存储添加都是通过top实现即data[top] = XXX
  • free:记录释放的data[index]的数量,当top=max时需要添加新数据时就需要通过判断free的大小查看在top之下有无释放了的空闲空间
  • **data:具体存放数据的地方——也是整个vector的关键,注意到这里的data类型,说明data存储的指针类型是可变的——这也是vector的巧妙设计之一
  • VECTOR_Key key:函数指针用于将一个数据元素转变为key进行比较
  • VECTOR_Compare compare:函数指针用于比较两个key是否相同

两个指针的使用主要在vector的创建之中,下面会结合例子进行说明

2.2 Vector的相关操作

说完结构体,那么就可以看看与之相关的操作了

2.2.1 VECTOR_Make

函数很简单,仅仅是简单的赋初值并返回对象Vector
在这里插入图片描述

但是真正的使用却略微讲究,在g_samgrImpl的初始化时是这样使用它创建vector的:
在这里插入图片描述
可以看到第一个参数是调用了函数GetServiceName,但是没有传入具体的serviceImpl,所以返回的仍然是NULL,但是我认为这样写肯定也是有用意的,不然为什么不直接写NULL,由于代码没有看完这里不妄加猜测,后面再做分析;第二个则是将string中的函数strcmp用于实现VECTOR_Compare。

2.2.2 VECTOR_Clear

函数功能:对于一个vector结构体空间的释放,先调用SAMGR_Free将data所指的空间释放,再将属性清空
在这里插入图片描述

2.2.3 VECTOR_Add

非常重要的一个函数:将element添加到data[top]中
在这里插入图片描述

具体的函数流程如下:
1. 当top<max时,将element直接放入data[top]位置上,返回top++
2. 当top>=max时,因为可能存在free的空间,所以进行循环,当有空闲位置(data[i]==NULL)时将element放入,并返回位置i——通过registerService函数我们可以知道返回的i后面存储为serviceId,所以这里一定是返回的当前存储对象的位置
3. 倘若没有找到空闲的位置,则需要对vector进行空间扩展以容纳新的对象
4. 分配新空间——大小为原来空间+4
5. 将原来data中的数据拷贝到新空间中
6. 将新空间更新到vector中
7. 重复第一步的操作,注意这是是先返回top的位置表示当前存储element的位置,然后再++用于下一次对象存储的开始top起点

我们可以看到初始化的时候data是NULL,所以设计这样的添加函数能够最大化有效的利用空间

这里有个思考的小插曲:
在判断top>=max后,通过循环遍历查找是否有free的空间这个流程中。我想的是如果提前判断free是否<=0,<=0则没有空闲空间就不需要循环遍历直接往下开辟新空间进行赋值,而如果free>0则再进行for循环。
不知道这样在工程上是不是能够提高那么一点点效率

2.2.4 VECTOR_At

函数功能:根据所给的index返回data[index]
在这里插入图片描述
注意这里返回的对象也是指针,看到返回的类型是void指针类型,说明可以返回任意类型——也就是先前存入的指针类型——返回void指针可以使函数具有很好的通用性,各种类型的指针都能够通过vetor进行管理——但是使用的时候一定要使用正确的指针类型进行接收


2.2.5 VECTOR_Swap

在这里插入图片描述
该函数完成两件事:首先将index对应的element提取并返回;其次如果传入新的element则将其覆盖index对应的旧element,传入NULL则free++表示多一块空闲空间,将旧element对应位置指针置为NULL

2.2.6 VECTOR_Size&VECTOR_Num

在这里插入图片描述
这两个函数较为简单,一个返回当前vector的总空间大小,一个返回vector中存储管理了多少element

2.2.7 VECTOR_Find&VECTOR_FindByKey

两个函数放到一起讲,都是查找element对应index的函数

首先看到第一个函数
在这里插入图片描述
最后一行的三目运算比较有意思:
在调用函数VECTOR_FindByKey传入第二个参数的时候判断vector->key是否存在——这又要追溯到vector建立时该属性的初始化是使用传入的参数key——再往上找,在init的时候是使用GetServiceName,没有传入参数(NULL),因为init的是全部服务的samgrImpl所以不能给定某个确定的serviceName。但是每个serviceImpl中间有对应的service类型。

所以我猜想使用三目运算是为了适配不同级别vector的使用,如果key的类别可以统一则通过vector->key(element)将element转变为统一的key类型便于后面的查找和compare;如果没有统一的key,则还是传入element

然后我们看向第二个函数
在这里插入图片描述
从函数体的内容来看,循环给到的data数组,跳过NULL的data[i],如果存在vector->key则进行类型转换再进行data[i]与所给key的比较,否则直接进行比较;如果存在compare则再使用其进行比较;匹配成功则返回对应的位置i

这个函数使用的较多,在registerService中有使用,我们看看用法:

int16 VECTOR_FindByKey(Vector *vector, const void *key)

static BOOL RegisterService(Service *service){
	……
    int16 pos = VECTOR_FindByKey(&(samgr->services), (void *)service->GetName(service));
    ……
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在注册的时候将samgr->services传入(类型为Vector)并且传入了(void *)service->GetName(service))如果以broadcast为例,这里返回的是const char * “Broadcast”

这里又有点疑惑了,原来我们意味vector中存入的应该都是对应serviceImpl的指针,而传入的是一个字符串,字符串应该是无法和serviceImpl类型指针匹配的(难道是为了使用下面的strcmp?)

而且我们知道g_samgrImpl->services对应的key是没有初始化的,所以也不能进行统一的key类型转换,那么这里的查找就有些迷惑了

虽然在登记函数中使用的时候期望的是pos找不到返回-1——因为并没有注册该函数,这样使用的结果没有问题

可能还是代码的理解不够完全,后续有机会还会深入思考这个问题

3. 总结

  1. 总的来说,这个结构体和相关函数的设计非常巧妙,使用了通用类型void*提高了vector的普适性,使其成为管理service和feature的有利工具
  2. 使用了几个简单的属性:top max free将vector最重要的几个属性:总容量、已用空间、闲置空间都表示清楚,并且top还能够作为添加element时的data[top],可以说是很巧妙了
  3. data的动态分配空间也能够很好的利用空间
  4. key的类型转换很好的适配了不同场景下对于查找的需求

至此Vector讲解完毕,不妥之处,还望批评指正

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/243866
推荐阅读
  

闽ICP备14008679号