asn1c中使用双重指针来实现asn中的sequence和set
按照asn.1的定义,sequence和set是一系列数据的集合。在实际使用中,可能就是某一个结构体的集合。
sequence和set分为各个元素相同以及每个元素不相同。在asn1c中,当每个元素相同时,定义为SEQUENCE_OF,当每个元素有不相同时,定义为SEQUENCE,
在asn1c中,将sequence通过动态数组来实现。当添加元素时,会判断当前是否需要扩展内存,如果需要扩展,就扩展之前一倍的内存。
数据结构描述
sequence of的结构体定义:
#define A_SEQUENCE_OF(type) A_SET_OF(type)
#define A_SET_OF(type) \
struct { \
type **array; \
int count; /* Meaningful size */ \
int size; /* Allocated size */ \
void (*free)(type *); \
}
可以看到,sequence of相当于set of,set of中的type是通过宏的参数设置进来的,实际使用时应该将type参数设置为数据结构的类型。结构体中有一个双重指针array,这个双重指针是本篇文章要重点介绍的对象。通过下一小节【想数组中添加元素】可以更好的理解这里为什么是一个双重指针。
向数组中添加元素
#define ASN_SEQUENCE_ADD(headptr, ptr) asn_sequence_add((headptr), (ptr))
/*
* Add another element into the set.
*/
int asn_set_add(void *asn_set_of_x, void *ptr) {
asn_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x);
if(as == 0 || ptr == 0) {
errno = EINVAL; /* Invalid arguments */
return -1;
}
/*
* Make sure there's enough space to insert an element.
*/
if(as->count == as->size) {
int _newsize = as->size ? (as->size << 1) : 4;
void *_new_arr;
_new_arr = REALLOC(as->array, _newsize * sizeof(as->array[0]));
if(_new_arr) {
as->array = (void **)_new_arr;
as->size = _newsize;
} else {
/* ENOMEM */
return -1;
}
}
as->array[as->count++] = ptr;
return 0;
}
向数组中添加元素最主要的是第31行。这一行将array中的一个元素指向要添加到数组中的结构体指针。从这里可以看出来,array[]数组中的每个元素是指针,那么array就是双重指针啦。
上面代码中,18~28行,是判断是否需要扩展内存的操作。如果需要扩展内存,那么就扩展之前容量的一倍。这片代码中,最不好理解的就是第23行。这里将申请的一片内存的首地址转换成双重指针,然后让array指向这个双重指针。
如果单单从表面上理解,指针就是指针,怎么能转换成双重指针呢?但是从第31行的操作可以看出,上面申请的内存中,存放的不是普通的数据,而是指向要加入到数组中结构的指针。一个指针指向的地址中存放的是指针,那么这个指针当然是双重指针啦。
从第21行看出,这里申请的内存的大小是n个指针的内存大小,因为array是双重指针,arrar[0]是指针,那么sizeof(array[0])就是一个指针的内存大小。
将这里的动态数组以图的形式展示,可能更容易理解一些:
如上图所示,array[]的每个元素是一个指针,指向内存中某处存放实际数据的首地址。
所以,由于申请的内存实际上用来存放指针,所以指向这片申请出来的地址的指针就是双重指针。
重点彩蛋
这里还要重点关注一个地方,就是第8行代码,这里有一个强制类型转换:_A_SET_FROM_VOID
,这个是将实际的数据指针类型强制转换成了void类型的指针。为什么要这样强制转换呢?因为这样做了之后,就可以让这个动态数组中的每个元素类型不同。因为都将他们的指针转换成了void型指针,所以可以存放在数组中。
删除整个数组
删除数组自然就是要进行内存释放,asn1c中的代码如下:
void asn_set_empty(void *asn_set_of_x) {
asn_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x);
if(as) {
if(as->array) {
if(as->free) {
while(as->count--)
as->free(as->array[as->count]);
}
FREEMEM(as->array);
as->array = 0;
}
as->count = 0;
as->size = 0;
}
}
这里重点是第8行,调用了一个回调函数去释放内存。这里为什么不直接调用free函数释放呢?因为要释放的内存的指针指向的可能是一个结构体,而结构体中还有内存要释放,即这个内存可能是分散的,不是连续的一片内存。
动态数组实例代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include "asn_SEQUENCE_OF.h"
#include "asn_SET_OF.h"
typedef struct student
{
char name[10];
int age;
}stu_t;
int main(int argc, char *argv[])
{
A_SEQUENCE_OF(stu_t) list;
stu_t stu1={"xiaoming", 10};
stu_t stu2={"xiaozhang", 15};
ASN_SEQUENCE_ADD(&list, &stu1);
ASN_SEQUENCE_ADD(&list, &stu2);
printf("list cnt: %d\n", list.count);
printf("stu1.name: %s, stu1.age: %d,stu2.name: %s, stu2.age: %d\n",
list.array[0]->name, list.array[0]->age, list.array[1]->name, list.array[1]->age);
printf("demo\n");
return 0;
}