往期知识点整理
一、前言
在distributedschedule_samgr_lite\interfaces\kits\samgr\common.h
文件中提供了简化的vector容器和转换函数。并针对vector容器的操作函数进行设计。下面对相关代码进行分析。
二、头文件分析
vector结构体
typedef struct SimpleVector {
int16 max; //可存储的最大数据记录数。
int16 top; //存储的数据记录数量的峰值。
int16 free; //已释放的数据记录数。
void **data;//数据段指针
VECTOR_Key key;//函数指针,将数据元素转换为用于比较的键,默认值为NULL
//1 表示key1大于key2
//0 表示key1等于key2
//-1 表示key1小于key2
//默认值为NULL
VECTOR_Compare compare;//函数指针,指向相应的键值比较函数
} Vector;
宏定义
//对于T类型的对象,获取它的member成员相较于起始地址的偏移量
#define GET_OFFSIZE(T, member) (long)((char *)&(((T *)(0))->member))
/*
1.首先获取member对于T的起始地址的偏移量
2.获取ptr-偏移量的地址值,然后转化为T类型
*/
@Ptr:成员变量的地址
@T:目标类型
@member:将@Ptr的名称指定为T成员变量
#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
文件中。
/*
函数功能:创建vector对象
函数参数:@key:函数指针,指向将数据元素转换为键值的函数
@compare:函数指针,指向比较两个元素的函数
函数返回:返回一个vector对象
详细描述:初始化vector的成员,并给函数指针赋值
*/
Vector VECTOR_Make(VECTOR_Key key, VECTOR_Compare compare)
{
//初始化vector成员,并给函数指针赋值
Vector vector = {0, 0, 0, NULL, key, compare};
return vector;
}
/*
函数功能:清空vector的内容
函数参数:@vector:vector对象
详细描述:释放vector的data指向的内存,并将vector对象的各个成员置为初始值。
*/
void VECTOR_Clear(Vector *vector)
{
//参数检查
if (vector == NULL) {
return;
}
if (vector->data == NULL) {
return;
}
//释放data指向的内存空间
SAMGR_Free(vector->data);
//标记值全部归0
vector->max = 0;
vector->top = 0;
vector->free = 0;
vector->data = NULL;
}
vector添加元素操作
/*
函数功能:在vector对象中添加新的元素
函数参数:@vector: vector对象
@element: 待添加的元素
函数返回:成功返回新加入的元素的下标,失败返回INVALID_INDEX
详细描述:
1.将元素添加到vector中,首先做参数检查。
2.判断vector中data是否有空闲空间,如果没有空闲空间,先遍历data中为NULL的元素,
若找到便将element加入后返回,没找到就申请新的空间。
3.最后将element加入data中,返回下标。
*/
int16 VECTOR_Add(Vector *vector, void *element)
{
//参数检查
if (vector == NULL || element == NULL) {
return INVALID_INDEX;
}
//无空闲空间
if (vector->top >= vector->max) {
int16 i;
//优先使用已释放的数据元素,从后往前遍历
for (i = vector->top - (int16)1; i >= 0; --i) {
if (vector->data[i] == NULL) {
//若找到一个指向==NULL的位置,则将元素插入
vector->data[i] = element;//data[i]指向element对应的内存空间
//更新已释放数,减一。代表重用了一块被释放的区域
vector->free--;
//返回插入的下标
return i;
}
}
//不存在可重用的位置,需申请新的空间
//max占用两个字节,判断增长是否会导致溢出
if (vector->max + GROW_STEP < 0) {
//容量达到上限,返回INVALID_INDEX
return INVALID_INDEX;
}
//重新申请新的空间,大小为当前容量max+增长步长GROW_STEP
void **data = (void **)SAMGR_Malloc(sizeof(void *) * (vector->max + GROW_STEP));
if (data == NULL) {
//内存分配失败
return INVALID_INDEX;
}
//若data指向一块内存,则需要拷贝
if (vector->data != NULL) {
//将源data中的数据拷贝到新的data中
(void)memcpy_s(data, sizeof(void *) * (vector->max + GROW_STEP),
vector->data, sizeof(void *) * vector->max);
//释放旧的data
SAMGR_Free(vector->data);
}
vector->data = data;//更新数据段
vector->max += GROW_STEP;//更新最大容量
}
//有空闲空间,将element加入
vector->data[vector->top] = element;
return vector->top++;//先返回当前元素的下标,再自增
}
根据下标获取元素
/*
函数功能:获取vector中指定下标的元素
函数参数:@vector: vector对象
@index: 指定下标
函数返回:成功即返回指定位置的元素,失败则返回NULL
*/
void *VECTOR_At(Vector *vector, int16 index)
{
//参数检查
if (vector == NULL || vector->top <= index || index < 0) {
return NULL;
}
//返回指定位置的元素
return vector->data[index];
}
vector中的元素交换操作,当与NULL交换时可用于删除元素
/*
函数功能:交换指定下标的元素
函数参数:@vector: vector对象
@index: 指定索引
@element: 指定元素
函数返回:成功 返回被替换的元素,失败 返回NULL
详细描述:
1.函数调用传入三个参数,待操作的vector对象、元素索引下标、当前元素
2.首先进行参数检查,若检查失败就返回NULL
3.判断当前元素是否为NULL,若为NULL 则free+1.
4.当element指向NULL时,即当前替换操作变为删除指定元素的操作
5.在替换前先保存旧的元素指针,再替换,并返回旧元素指针。
*/
void *VECTOR_Swap(Vector *vector, int16 index, void *element)
{
//参数检查
if (vector == NULL || vector->top <= index || index < 0) {
return NULL;
}
//若当前元素为NULL则free数+1
if (element == NULL) {
vector->free++;
}
//保存当前索引中对应的旧元素
void *oldElement = vector->data[index];
vector->data[index] = element;//交换
return oldElement;
}
其余函数后续再进一步分析。