以前,写过一个C 语言泛型数组实现,但在实际的使用过程中,发现了更加易用的模式,进行了改进。这个数组封装作为一个最基础的结构,在此之上实现了动态数组,队列,数组映射等。还利用这些数据结构实现了一个简单的json解析器,已经成为C工具箱的最基础工具,可以验证这些结构的可用性和稳定性,以后将逐步介绍。
这个泛型数组分为代码实现和宏定义两部分,主要完成以下功能。
- 可存储任意数据类型数据
- 带有数据长度
- 一套便捷使用的宏定义接口
- 可以在堆上,也可以在栈上使用
首先看代码实现部分,非常的简单。
typedef struct
{
/** Elements memory data */
void* data;
/** Elements count */
int length;
}
Array;
typedef struct
{
/**
* Array space and data space in one malloc
* Array data hold the offset of malloc return address
*/
Array* (*Create)(int typeSize, int length);
}
_AArray_;
extern _AArray_ AArray[1];
static Array* Create(int typeSize, int length)
{
Array* array = (Array*) malloc(sizeof(Array) + typeSize * length);
array->data = (char*) array + sizeof(Array);
array->length = length;
return array;
}
_AArray_ AArray[1] =
{
Create,
};
Create是在堆上分配空间,这里我们采用了折叠空间的方法,是的Array本身的空间和元素的空间一次性分配,用过data指针持有元素空间的偏移地址。这样的好处就是分配和释放只需要一次。
然后,是一组宏定义的访问接口。
/** The type is the array element type */
#define Array(type) Array
/**
* The type is element type
*/
#define AArray_GetData(array, type) \
(type*) ((array)->data)
/**
* return element
*/
#define AArray_Get(array, index, type) \
(AArray_GetData(array, type))[index]
/**
* return element ptr
*/
#define AArray_GetPtr(array, index, type) \
(AArray_GetData(array, type) + index)
/**
* set element
*/
#define AArray_Set(array, index, element, type) \
AArray_Get(array, index, type) = element
标示性宏定义
#define Array(type) Array 的意义就是在定义的时候,可以明确Array元素的类型,虽然编译器没有像泛型那样的类型检查,但C中可以增加可读性。然后就是一组对元素的get,set访问方法。那么,Get和GetPtr的区别就在于。元素是指针的时候Get返回指针,元素是结构对象的时候Get回返回对象,这样会产生栈上的结构对象复制。而这个时候使用GetPtr能获得元素对象的指针,不会复制元素对象的结构内容。
特别介绍两个宏定义。
/**
* Create Array composite literal
*/
#define AArray_Create(type, length, ...) \
(Array[1]) \
{ \
(type[length]) { __VA_ARGS__ }, \
length, \
}
/**
* Init constant Array
*/
#define AArray_Init(type, length, ...) \
{ \
(type[length]) { __VA_ARGS__ }, \
length, \
}
这两个宏定义,都是在栈上分配数组空间并初始化。使用了C99变参宏替换,这样数组元素可以传入变参数据。另外,第一个使用了C99的
复合字面量。这两个定义的使用区别在于,第一个可以赋值给变量,第二个是在结构对象定义中嵌入字面量。