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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值