C 实现泛型ArrayList数组

    ArrayList数组是基石,是完成更复杂功能的基础。几乎所有的语言所提供工具库中,都含有ArrayList数组结构,唯有C的标准库中没有提供。我的实现是工具箱的一部分,上层已经依赖完成诸多功能。所以,正确性和效率是经过验证的。


    实现的核心是利用了标准库realloc的特性,实现之前我也参看过很多开源的代码和思路。使用realloc并非是最好的选择,但是最简单最容易想到的实现。可以了解一些以前写的这篇文章,realloc 使用和思考。依赖C语言泛型Array数组封装。ArrayList完成以下功能:

  • 可以存放任意数据类型
  • 在需要的时候动态扩充内存
  • 可以自定义内存扩充策略
  • 一套元素访问接口
  • 一套快捷的宏定义访问
  • 可缩放的容量控制

    首先看定义
    
typedef struct
{
	/**
	 * Increase memory space default 10
	 */
	int   increment;

	/**
	 * Hold ArrayList memory data
	 * the length is ArrayList memory capacity
	 */
	Array array[1];

	/** Element sizeof  */
	int	  typeSize;

	/** Element count */
	int   size;
}
ArrayList;

    真正的数据容量被Array持有,size就是元素的个数,容量是Array的length。typeSize代表了元素的结构大小。

    元素访问方法如下:
    
typedef struct
{
	/** The typeSize is sizeof element */
	ArrayList* (*Create)            (int typeSize);
	void       (*Init)              (int typeSize, ArrayList* outArrayList);

	ArrayList* (*CreateWithSize)    (int typeSize, int size);
	void       (*InitWithSize)      (int typeSize, int size, ArrayList* outArrayList);

	ArrayList* (*CreateWithCapacity)(int typeSize, int capacity);
	void       (*InitWithCapacity)  (int typeSize, int capacity, ArrayList* outArrayList);

	/**
	 * Release member memory space
	 */
	void       (*Release)           (ArrayList*  arrayList);

	/**
	 * Increase one element memory (typeSize data) appended to ArrayList
	 * return elementPtr point the increase element
	 */
	void*      (*GetAdd)            (ArrayList* arrayList);

	/**
	 * Increase one element memory (typeSize data) at the index
	 * and move index behind elements one position
	 *
	 * index is which in [0, size - 1]
	 * return elementPtr point the increase element
	 */
	void*      (*GetInsert)         (ArrayList* arrayList, int index);

	/**
	 * Copy element data into getAdd memory
	 * elementPtr point to element
	 * return elementPtr in ArrayList
	 */
	void*      (*Add)               (ArrayList* arrayList, void* elementPtr);

	/**
	 * Copy element data into getInsert memory
	 * elementPtr point to element
	 * return elementPtr in ArrayList
	 */
	void*      (*Insert)            (ArrayList* arrayList, int index, void* elementPtr);

	/**
	 * Get elementPtr point to element in [0, size - 1] of ArrayList
	 *
	void*      (*Get)               (ArrayList* arrayList, int index);
	 */

	/**
	 * Copy element data into index in [0, size - 1] of ArrayList
	 * elementPtr point to element
	 *
	void       (*Set)               (ArrayList* arrayList, int index, void* elementPtr);
	 */

	/**
	 * Remove index of element and move behind elements
	 */
	void       (*Remove)            (ArrayList* arrayList, int index);

	/**
	 * Remove a range of elements
	 */
	void       (*RemoveRange)       (ArrayList* arrayList, int fromIndex, int toIndex);

	/**
	 * Remove index of element and replace last element to the index
	 */
	void       (*RemoveByEnd)       (ArrayList* arrayList, int index);

	/**
	 * Clear size to 0 and memory data rest to 0, keep memory space
	 */
	void       (*Clear)             (ArrayList* arrayList);

	/**
	 * Release not used memory data which is after ArrayList size
	 * if ArrayList size is 0 will be free all memory data
	 */
	void       (*Shrink)            (ArrayList* arrayList);

	/**
	 * Set ArrayList size and unused memory initialized 0
	 */
	void       (*SetSize)           (ArrayList* arrayList, int size);

	/**
	 * Set ArrayList capacity and unused memory initialized 0
	 */
	void       (*SetCapacity)       (ArrayList* arrayList, int capacity);
}
_AArrayList_;

extern _AArrayList_ AArrayList[1];

实现如下:
#define CheckIndex(tag, arrayList, index) \
	ALog_A(((index) < (arrayList)->size && (index) >= 0),  "ArrayList " tag " error, index = %d, size = %d", index, arrayList->size);

#define CheckInsertIndex(tag, arrayList, index) \
	ALog_A(((index) <= (arrayList)->size && (index) >= 0), "ArrayList " tag " error, index = %d, size = %d", index, arrayList->size);


static void AddCapacity(ArrayList* arrayList, int increment)
{
	ALog_A(increment > 0, "ArrayList error, increment = %d can not <= 0", increment);
	void* data = realloc(arrayList->array->data, (increment + arrayList->array->length) * arrayList->typeSize);

	ALog_A
	(
	    data,
		"ArrayList error, unable to realloc memory, size = %d, length = %d, increment = %d",
		arrayList->size, arrayList->array->length, increment
	);

	/** set increment data to 0, but it seems not very necessary
	memset
	(
	    (char*) data + arrayList->typeSize * arrayList->array->length,
		 0,
		 arrayList->typeSize * increment
    );
    */

	arrayList->array->data    = data;
	arrayList->array->length += increment;
}

static void* GetAdd(ArrayList* arrayList)
{
	if (arrayList->size == arrayList->array->length)
	{
		AddCapacity(arrayList, arrayList->increment);
	}

    return (char*) arrayList->array->data + arrayList->typeSize * (arrayList->size++);
}

static void* GetInsert(ArrayList* arrayList, int index)
{
	// insert index can equal size
	CheckInsertIndex("GetInsert", arrayList, index);

	if (arrayList->size == arrayList->array->length)
	{
		AddCapacity(arrayList, arrayList->increment);
	}

	void* from  = (char*) arrayList->array->data + arrayList->typeSize * index;
	void* to    = (char*) from                   + arrayList->typeSize;

	memmove(to, from, arrayList->typeSize * (arrayList->size - index));
	arrayList->size++;

	return from;
}

static void* Add(ArrayList* arrayList, void* elementPtr)
{
	void* ptr = GetAdd(arrayList);
    memcpy(ptr, elementPtr, arrayList->typeSize);
    return ptr;
}

static void* Insert(ArrayList* arrayList, int index, void* elementPtr)
{
	void* ptr = GetInsert(arrayList, index);
	memcpy(ptr, elementPtr, arrayList->typeSize);
	return ptr;
}


/** use macro instead
static void* Get(ArrayList* arrayList, int index)
{
	CheckIndex("Get", arrayList, index);
	return (char*) arrayList->array->data + arrayList->typeSize * index;
}
*/


/** use macro instead
static void Set(ArrayList* arrayList, int index, void* elementPtr)
{
	CheckIndex("Set", arrayList, index);
	memcpy((char*) arrayList->array->data + arrayList->typeSize * index, elementPtr, arrayList->typeSize);
}
*/


static void Remove(ArrayList* arrayList, int index)
{
	CheckIndex("Remove", arrayList, index);

	int endIndex    = arrayList->size - 1;
	arrayList->size = endIndex;

	if (index == endIndex)
	{
		// remove the last element only set 0, but it seems not very necessary
		// memset((char*) arrayList->array->data + arrayList->typeSize * index, 0, arrayList->typeSize);
	}
	else
	{
		void* to    = (char*) arrayList->array->data + arrayList->typeSize * index;
		void* from  = (char*) to                     + arrayList->typeSize;

		// move between endIndex and index element
		memmove(to, from, arrayList->typeSize * (endIndex - index));

		// set last element 0, but it seems not very necessary
		// memset((char*) arrayList->array->data + arrayList->typeSize * arrayList->size, 0, arrayList->typeSize);
	}
}


static void RemoveRange(ArrayList* arrayList, int fromIndex, int toIndex)
{
	CheckIndex("RemoveRange", arrayList, fromIndex);
	CheckIndex("RemoveRange", arrayList, toIndex);
	ALog_A(toIndex > fromIndex, "ArrayList RemoveRange toIndex must more than fromIndex");

	int num          = toIndex         - fromIndex + 1;
	int endIndex     = arrayList->size - 1;

	arrayList->size -= num;

	if (toIndex == endIndex)
	{
		// range reach the end, but it seems not very necessary
		// memset((char*) arrayList->array->data + arrayList->typeSize * fromIndex, 0, arrayList->typeSize * num);
	}
	else
	{
		void* to    = (char*) arrayList->array->data + arrayList->typeSize * fromIndex;
		void* from  = (char*) to                     + arrayList->typeSize * num;

		// move between endIndex and toIndex element
		memmove(to, from, arrayList->typeSize * (endIndex - toIndex));

		// set last range elements 0, but it seems not very necessary
		// memset((char*) arrayList->array->data + arrayList->typeSize * arrayList->size, 0, arrayList->typeSize * num);
	}
}

static void RemoveByEnd(ArrayList* arrayList, int index)
{
	CheckIndex("removeByLast", arrayList, index);

	int endIndex    = arrayList->size - 1;
	arrayList->size = endIndex;

	if (index == endIndex)
	{
		// remove the last element only set 0, but it seems not very necessary
		// memset((char*) arrayList->array->data + arrayList->typeSize * index, 0, arrayList->typeSize);
	}
	else
	{
		memcpy
		(
			(char*) arrayList->array->data + arrayList->typeSize * index,
			(char*) arrayList->array->data + arrayList->typeSize * endIndex,
			arrayList->typeSize
		);
	}
}


static void Clear(ArrayList* arrayList)
{
	arrayList->size = 0;

	// set data 0, but it seems not very necessary
	// memset(arrayList->array->data, 0, arrayList->typeSize * arrayList->size);
}


static void Shrink(ArrayList* arrayList)
{
	if (arrayList->size == 0)
	{
		arrayList->array->length = 0;
		free(arrayList->array->data);
		arrayList->array->data   = NULL;
	}
	else
	{
		void* data = realloc(arrayList->array->data, arrayList->size * arrayList->typeSize);
		ALog_A(data, "ArrayList Shrink error, size = %d ", arrayList->size);

		arrayList->array->data   = data;
		arrayList->array->length = arrayList->size;
	}
}


static void Release(ArrayList* arrayList)
{
	arrayList->size          = 0;
	arrayList->array->length = 0;
	free(arrayList->array->data);
	arrayList->array->data   = NULL;
}



static void SetSize(ArrayList* arrayList, int size)
{
	arrayList->size = size;

	if (arrayList->array->length >= size)
	{
		return;
	}

	AddCapacity(arrayList, size - arrayList->array->length);
}

static void SetCapacity(ArrayList* arrayList, int capacity)
{
	if (arrayList->array->length >= capacity)
	{
		return;
	}
	
	AddCapacity(arrayList, capacity - arrayList->array->length);
}



#define InitArrayList(typeSize, arrayList) \
	arrayList->array->length = 0;          \
	arrayList->array->data   = NULL;       \
	arrayList->typeSize      = typeSize;   \
	arrayList->size          = 0;          \
	arrayList->increment     = 10


static ArrayList* Create(int typeSize)
{
	ArrayList* arrayList = (ArrayList*) malloc(sizeof(ArrayList));
	InitArrayList(typeSize, arrayList);
	
	return arrayList;
}

static void init(int typeSize, ArrayList* outArrayList)
{
	InitArrayList(typeSize, outArrayList);
}

static ArrayList* CreateWithSize(int typeSize, int size)
{
	ArrayList* arrayList = Create(typeSize);
	SetSize(arrayList, size);

	return arrayList;
}

static void InitWithSize(int typeSize, int size, ArrayList* outArrayList)
{
	InitArrayList(typeSize, outArrayList);
	SetSize(outArrayList, size);
}


static ArrayList* CreateWithCapacity(int typeSize, int capacity)
{
	ArrayList* arrayList = Create(typeSize);
	SetCapacity(arrayList, capacity);
	
	return arrayList;
}

static void InitWithCapacity(int typeSize, int capacity, ArrayList* outArrayList)
{
	InitArrayList(typeSize, outArrayList);
	SetCapacity(outArrayList, capacity);
}


_AArrayList_ AArrayList[1] =
{
	Create,
	init,
	CreateWithSize,
	InitWithSize,
	CreateWithCapacity,
	InitWithCapacity,

	Release,

	GetAdd,
	GetInsert,
	Add,
	Insert,
//	get,
//	set,
	Remove,
	RemoveRange,
	RemoveByEnd,
	Clear,
	Shrink,
	SetSize,
	SetCapacity,
};


#undef CheckIndex
#undef CheckInsertIndex
#undef InitArrayList

   代码注释明确,写法统一,也没有什么好说的,依赖标准库的函数。Array的定义,在上篇文章里有说。下面还有一套宏定义的快捷操作。

/** The type is the ArrayList element type */
#define ArrayList(type) ArrayList

/**
 * The type is element type
 */
#define AArrayList_GetData(arrayList, type) \
	(type*) ((arrayList)->array->data)

/**
 * Instead of AArrayList->get for quick iterate element
 * return element
 */
#define AArrayList_Get(arrayList, index, type) \
	(AArrayList_GetData(arrayList, type))[index]

/**
 * Instead of AArrayList->get for quick iterate element
 * return element ptr
 */
#define AArrayList_GetPtr(arrayList, index, type) \
	(AArrayList_GetData(arrayList, type) + index)

/**
 * Instead of AArrayList->set for quick set element
 */
#define AArrayList_Set(arrayList, index, element, type) \
	AArrayList_Get(arrayList, index, type) = element


/**
 * Init constant ArrayList
 */
#define AArayList_Init(type, increment) \
	{                                   \
		increment,                      \
		{                               \
			NULL,                       \
			0,                          \
		},                              \
		sizeof(type),                   \
		0,                              \
	}

/**
 * Init constant ArrayList with fixed capacity, unable to expansion capacity
 */
#define AArayList_InitFixed(type, capacity, size, ...) \
	{                                                  \
		0,                                             \
		AArray_Init(type, capacity, __VA_ARGS__),      \
		sizeof(type),                                  \
		size,                                          \
	}


    这里可以看到,我的一个写法。数据属性和数据操作的方法,我分为两个结构体来承载,并且使用单例容器来存放方法。我使用了一套面向对象的C编程的写法。以后会单独介绍。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值