数据结构——动态数组(C语言详述通用动态数组)

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《系统程序员成长计划》

一、概念

1、数组的相关描述?

1️⃣什么是数组?
  相同类型元素的集合,分配在一块连续的内存空间。
2️⃣数组的分类:(根据数组对象长度在哪个阶段确定)
  ① 静态数组
    由系统在内存中栈区分配的空间,编译时确定数组长度,运行期间不可改,运行后系统自动释放
  ② 动态数组
    由malloc()在内存中堆区分配的空间,运行时确定数组长度,运行期间可更改,需人工手动释放

2、静态数组

1️⃣定义与注意点

一维数组 二维数组
定义 dataType arrayName[arraySize] = {initialize}; dataType arrayName[arraySize] [arraySize] = {initialize};
描述 dataType :数据类型,如:int 、float等.
arrayName:数组名,与变量名命名规则相同。
arraySize:数组大小(下标),必须为常量,从0开始。
initialize:初始化数值,(数组元素不可超过数组长度)
1.全部赋值(可不指定数组程度,会自动计算)2.部分赋值
注意 ①一维大小可省,二维不可省
②按行顺序存储(行之间可用大括号区分)
①数组作为参数时,传递的是数组的起始地址
②数组下标是从0开始

  2️⃣示例

#include<stdio.h>

int global_a[] = {0,1,2,3};

int main(int argc,char **argv)
{
	static int static_a[] = {0,1,2,3};
	/* 一维数组 */
	int local_a[6] = {0,1,2,3,4,5};		//全部赋值
	int local_b[] = {0,1,2,3};		//全部赋值,省略数组长度
	int local_c[6] = {0,1,2,3};		//部分赋值
	//int local_d[3] = {0,1,2,3};		//赋值越界, excess elements in array initializer

	/* 二维数组 */
	int local_da[2][2]={0,1,2,3};		//全部赋值
	int local_db[2][2]={{0,1},{2,3}};	//全部赋值
	int local_dc[][2]={0,1,2,3};		//全部赋值,省略一维数组长度
	int local_dd[][2]= {0,1};		//部分赋值	

	return 0;
}

3、动态数组

1️⃣简述
  ①定义指针对象,用于指向内存空间。
  ②malloc函数,在内存堆区开辟空间。
2️⃣示例
  (实例一)
  ①源程序array.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MIN_PRE_ALLOCATE_NR 1

#ifndef debug_printf
#define debug_printf printf
#endif

int main(int argc,char **argv)
{
	int arrayLen = 3;								//动态数组初始化长度
	int *darray = NULL;								//指向动态数组指针						
	int local_a[10] = {0,1,2,3,4,5,6,7,8,9};		//一维数组,全部赋值
	int i = 0;

	darray = (int *)malloc(sizeof(int)*arrayLen);
	if(!darray)
	{
		debug_printf("malloc error!\n");
		exit(1);
	}

	/* 查看不同的表示方法的字节大小 */
	debug_printf("%ld %ld %ld\n",sizeof(local_a),sizeof(local_a[0]),sizeof(int));
	debug_printf("%d\n",arrayLen);

	/* 根据静态数组大小改变动态数组存储空间大小 */
	while (sizeof(local_a)/sizeof(local_a[0]) > arrayLen)
	{
		//扩大1.5倍,不直接*1.5而采用这边这样方式是因为这样计算速度更快,后面加上一个常数确保若arrayLen 为0存在的错误
		arrayLen = arrayLen + (arrayLen >> 1) + MIN_PRE_ALLOCATE_NR;	
		darray = (int *)realloc((void *)darray,sizeof(int)*arrayLen);
		memset(darray,0,sizeof(int)*arrayLen);
		debug_printf("%d\n",arrayLen);
	}
	
	/* 将静态数组中数据复制到动态数组中 */
	memcpy(darray,&local_a,sizeof(int)*sizeof(local_a)/sizeof(local_a[0]));

	/* 将动态数组中所有数据输出 */
	for(i = 0;i < sizeof(local_a)/sizeof(local_a[0]);i++)
	{
		/* 作为参数时 数组与指针的写法是可互相替换 */
		debug_printf("%d ",darray[i]);			//数组写法,
		// debug_printf("%d ",(darray + 0)[i]);	//数组写法,(darray + n),表示darray偏移第n个字符的起始地址	
		// debug_printf("%d ",*(darray+i));		//指针写法,
		// debug_printf("%d ",*darray+i);		//指针写法,

	}
	debug_printf("\n");

	free(darray);
	return 0;
}

  ②编译运行及结果

xsndz@Linux:~/Desktop$ gcc array.c  -o array
xsndz@Linux:~/Desktop$ ./array 
40 4 4
3
5
8
13
0 1 2 3 4 5 6 7 8 9 

  (实例二)
  ①源程序array2.c

#include <stdio.h>
#include <stdlib.h>
int main(){
    int arrLen;  // 数组长度
    int *array;  // 数组指针
    int i;  // 数组下标
    printf("输入数组长度:");
    scanf("%d", &arrLen);
    // 动态分配内存空间,如果失败就退出程序
    array = (int*)malloc( arrLen*sizeof(int) );
    if(!array)
    {
        printf("创建数组失败!\n");
        exit(1); 
    }
    // 向内存中写入数据
    for(i=0; i<arrLen; i++)
    {
        array[i] = i+1;
    } 
    // 循环输出数组元素
    for(i=0; i<arrLen; i++)
    {
        printf("%d  ", array[i]);
    }
    printf("\n");
    free(array);  
    return 0;
}
xsndz@Linux:~/Desktop$ gcc array2.c  -o array
xsndz@Linux:~/Desktop$ ./array 
输入数组长度:20
1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20 

二、通用动态数组

1、文件结构

.
├── darray.c					//动态数组源文件
├── darray.h					//动态数组头文件
├── main.c						//主循环函数
├── Makefile			
└── typedef.h					//通用数据类型头文件

2、动态数组结构体

#######   darray.h   #######  
struct _DArray;
typedef struct _DArray DArray;
#######   darray.c   #######  
/**
 * @struct:	动态数组结构体
*/
struct _DArray
{
	void** data;					//数据
	size_t size;					//实际使用数组大小
	size_t alloc_size;				//已分配数组大小

	void* data_destroy_ctx;			//销毁函数中上下文变量
	DataDestroyFunc data_destroy;	//销毁函数指针,为指向销毁函数
};

  按照封装思想,隐藏数据结构,由于该数据结构内外都要使用,故定义放在源文件中,声明放在头文件中。
    详细可查看:读书笔记 ——《系统程序员成长计划》篇2:封装
  因为是通用动态数组,故选择存放的变量数据类型为void*指针,可是这边为什么是void**指针呢?
    因为此处只是作为指针,用于指向具体动态数组空间,但由于数据对象就是void*指针,所以void**指针

3、动态数组 创建与销毁函数

#######   darray.c   #######  
/**
 * @function:动态数组 创建函数
 * 
 * @param data_destroy: 使用者编写的销毁函数的函数指针
 * @param ctx: 			销毁函数中的上下文数据
 * 
 * @return:				动态数组对象
 * @description:
 * 		开辟一块动态数组结构体大小的空间,并赋初始值。
*/
DArray* darray_create(DataDestroyFunc data_destroy, void* ctx)
{
	DArray* thiz = malloc(sizeof(DArray));

	if(thiz != NULL)
	{

		thiz->data  = NULL;
		thiz->size  = 0;
		thiz->alloc_size = 0;
		thiz->data_destroy = data_destroy;
		thiz->data_destroy_ctx = ctx;
	}

	return thiz;
}

/**
 * @function: 动态数组 数据销毁函数
 * 
 * @param thiz:		动态数组对象
 * @param data:		数据
 * 
 * @return: 无
*/
static void darray_destroy_data(DArray* thiz, void* data)
{
	if(thiz->data_destroy != NULL)
	{
		thiz->data_destroy(thiz->data_destroy_ctx, data);
	}

	return;
}

/**
 * @function:动态数组 销毁函数
 * 
 * @param thiz: 动态数组对象
 * 
 * @return: 当前动态数组的长度
*/
void darray_destroy(DArray* thiz)
{
	size_t i = 0;
	
	if(thiz != NULL)
	{
		for(i = 0; i < thiz->size; i++)
		{
			darray_destroy_data(thiz, thiz->data[i]);
		}
		
		SAFE_FREE(thiz->data);
		SAFE_FREE(thiz);
	}

	return;
}

  只要有对象的创建就要有对象的销毁!!!
  动态数组创建函数:用于开辟一块空间,作为动态数组对象,存放动态数组相关信息。
  注意:
    仅创建该动态数据对象,具体的动态数组数据空间用指针指向,根据需求对空间进行分配。
    创建函数形参用于传入销毁函数指针和销毁函数的参数。(这样面对不同数据类型的数据,可由调用者灵活的进行销毁)
  动态数组销毁函数:用于清空动态数组空间数据内容并释放动态数组空间和释放动态数组对象。
  注意:
    销毁动态数组元素的数据单独用一个静态函数表示。(仅在该文件中起作用,不对外开放接口,同样也是封装的思想)
    释放空间时,注意要释放两块存储空间,一个是动态数组对象空间,一个是动态数组数据空间

4、动态数组 空间扩展与缩小函数

#######   darray.c   #######
#define MIN_PRE_ALLOCATE_NR 10
/**
 * @function: 动态数组 扩大存储空间函数
 * 
 * @param thiz: 动态数组对象
 * @param need: 所需动态数组大小
 * 
 * @return: 执行状态
 * @description:
 * 		扩展数组时,并不是一次扩展一个元素,而是多个元素,1.5倍为作者的经验所得
*/
static Ret darray_expand(DArray* thiz, size_t need)
{
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	/* 已分配动态数组大小不够,重新分配动态数组大小 */
	if((thiz->size + need) > thiz->alloc_size)	
	{
		size_t alloc_size = thiz->alloc_size + (thiz->alloc_size>>1) + MIN_PRE_ALLOCATE_NR;

		void** data = (void**)realloc(thiz->data, sizeof(void*) * alloc_size);
		if(data != NULL)
		{
			thiz->data = data;
			thiz->alloc_size = alloc_size;
		}
	}

	return ((thiz->size + need) <= thiz->alloc_size) ? RET_OK : RET_FAIL;
}

/**
 * @function: 动态数组 缩小存储空间函数
 * 
 * @param thiz:动态数组对象
 * 
 * @return:执行状态
 * @description:
 * 当  实际空间大小的一半 还大于当前数组大小 且 实际空间大小 要大于 固定值 (预防为0的情况)时,减小为有效空间的1.5倍。
 * 注:删除元素时也并不是马上释放空间,而是等空闲空间高于某个值才释放它们。
 * 
*/
static Ret darray_shrink(DArray* thiz)
{
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	/* 已分配数组空间大小太大 */
	if((thiz->size < (thiz->alloc_size >> 1)) && (thiz->alloc_size > MIN_PRE_ALLOCATE_NR))
	{
		size_t alloc_size = thiz->size + (thiz->size >> 1);

		void** data = (void**)realloc(thiz->data, sizeof(void*) * alloc_size);
		if(data != NULL)
		{
			thiz->data = data;
			thiz->alloc_size = alloc_size;
		}
	}

	return RET_OK;
}

  动态数组空间扩展函数:若当前已使用大小+所需大小 > 已分配大小,则需扩展动态数组空间大小(扩大为已分配用的1.5倍)。
    第一次扩展空间的大小有一个最小空间值,此处为10.
  动态数组空间缩小函数:若当前已使用大小<已分配大小的二分之一且要已分配要大于最小空间,则需缩小动态数组空间大小(缩小为已使用的1.5倍)

5、动态数组 插入函数(指定插,头插,尾插)

#######   darray.c   #######
/**
 * @function: 动态数组 指定插入函数
 * 
 * @param thiz:  动态数组对象
 * @param index: 要插入的下标位置
 * @param data:  要插入的数据
 * 
 * @return : 执行状态
 * 
 * @description:
 * 	 注意:数组是从0开始的,故for循环中thiz->size无需+1
 * 		 但thiz->size表示的是动态数组当前数量,即之后必须加1
*/
Ret darray_insert(DArray* thiz, size_t index, void* data)
{
	Ret ret = RET_OOM;
	size_t cursor = index;
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	cursor = cursor < thiz->size ? cursor : thiz->size;		//若插入位置大于动态数组现有大小则插在现有大小后面

	if(darray_expand(thiz, 1) == RET_OK)
	{
		size_t i = 0;

		/* 将要插入位置以后的数据 向后移动一个单元 */
		for(i = thiz->size; i > cursor; i--)		
		{
			thiz->data[i] = thiz->data[i-1];
		}

		thiz->data[cursor] = data;
		thiz->size++;							
		
		ret = RET_OK;
	}

	return ret;
}

/**
 * @function: 动态数组  插入函数(头插法)
 * 
 * @param thiz:动态数组对象
 * @param data:数据内容
 * 
 * @return: 执行状态
*/
Ret darray_prepend(DArray* thiz, void* data)
{
	return darray_insert(thiz, 0, data);
}

/**
 * @function:动态数组 插入函数(尾插法)
 * 
 * @param thiz:动态数组
 * @param data:数据内容
 * 
 * @return: 执行状态
 * @description:
 * 		因为size_t 为无符号整数类型 故数值为最大
*/
Ret darray_append(DArray* thiz, void* data)
{
	return darray_insert(thiz, -1, data);//-1表示一个非常大的正数
}

  注意此处传入的数据是void *类型的数据。因为此处构建的是通用的动态数组,所以数据存储的是万能指针(void *)。
  插入模式分为三种:指定位置插,头插(0= 头部),尾插(-1=最大正数)。

6、动态数组删除指定位置元素

#######   darray.c   #######
/**
 * @function:动态数组 删除函数
 * 
 * @param thiz: 动态数组对象
 * @param index:下标数值
 * 
 * @return: 执行状态
*/
Ret darray_delete(DArray* thiz, size_t index)
{
	return_val_if_fail(thiz != NULL && thiz->size > index, RET_INVALID_PARAMS); 
	size_t i = 0;
	Ret ret = RET_OK;

	/* 删除指定节点数据 */
	darray_destroy_data(thiz, thiz->data[index]);			

	/* 数据前移 */
	for(i = index; (i+1) < thiz->size; i++)
	{
		thiz->data[i] = thiz->data[i+1];
	}

	thiz->size--;			//当前数量-1
	darray_shrink(thiz);	//检查是否需要缩小内存空间

	return RET_OK;
}

7、动态数组 获取指定下标数据函数

#######   darray.c   #######
/**
 * @function:动态数组  获取指定下标数据函数
 * 
 * @param thiz:  动态数组
 * @param index: 下标
 * @param data:	 数据
 * 
 * @return: 执行状态
 * 
 * @description:
 * 		int  data   		数据  
 * 		void *data  	data数据的指针
 * 		void **data 	data数据的指针的指针 
 * 
 * 		int data ;  第三个参数传入:(void**)&data)
 * 		&data为地址,强制转换为(void**)
 * 
*/
Ret darray_get_by_index(DArray* thiz, size_t index, void** data)
{
	return_val_if_fail(thiz != NULL && data != NULL && index < thiz->size, 
		RET_INVALID_PARAMS); 

	*data = thiz->data[index];

	return RET_OK;
}

  使用方法:
    定义变量,在函数的第三个参数传入该变量地址(由于该动态数组是通用的数据存的是指针,故记得强制类型转换为void**).
  此处传入的变量的地址(void **)&data,函数中*data表示指向该变量的数值从而将动态数组的数据赋值给变量(此处给的是void *类型数据,即:thiz->data[index]void *类型,*data也是void *类型)

8、动态数组 设置指定下标数据函数

#######   darray.c   #######
/**
 * @function:动态数组 设置指定下标数据函数
 * 
 * @param thiz:  动态数组对象
 * @param index: 下标
 * @param data:	 数据
 * 
 * @return: 执行状态
*/
Ret darray_set_by_index(DArray* thiz, size_t index, void* data)
{
	return_val_if_fail(thiz != NULL && index < thiz->size, 
		RET_INVALID_PARAMS); 

	thiz->data[index] = data;

	return RET_OK;
}

  注意:这边传入的数据同样应该是void*的指针类型,若传入的是其他类型应该强制类型转换为指针类型。

9、动态数组 获取数组数组长度

#######   darray.c   #######
/**
 * @function:动态数组 获取数组长度函数
 * 
 * @param thiz:动态数组对象
 * 
 * @return: 当前动态数组的长度
*/
size_t   darray_length(DArray* thiz)
{
	size_t length = 0;
	
	return_val_if_fail(thiz != NULL, 0);

	return thiz->size;
}

  用于获取数组对象中size成员.

10、动态数组 遍历访问函数(调用者可用于对遍历数据进行一定操作)

#######   darray.c   #######
/**
 * @function:动态数组 遍历函数
 * 
 * @param thiz: 动态数组对象
 * @param visit:回调函数
 * @param ctx:	回调函数上下文变量
 * 
 * @return: 当前动态数组的长度
*/
Ret darray_foreach(DArray* thiz, DataVisitFunc visit, void* ctx)
{
	return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
	size_t i = 0;	
	Ret ret = RET_OK;
	
	/* 遍历动态数组 */
	for(i = 0; i < thiz->size; i++)
	{
		ret = visit(ctx, thiz->data[i]);
	}

	return ret;
}

  遍历动态数组数据,以调用者想要的形式进行访问动态数组的数据(使用函数指针决定具体访问类型)

11、动态数组 查找函数(调用者可用于获取对应数据内容)

#######   darray.c   #######
/**
 * @function:动态数组 查找函数
 * 
 * @param thiz: 动态数组对象
 * @param cmp:  回调函数
 * @param ctx:	回调函数上下文变量(要查找的数值)
 * 
 * @return: 对应数据的下标
 * 
 * @desciption:
 * 		遍历动态数组,查找动态数组中存不存在数据(ctx),若找到则直接跳出并返回对应下标
*/
int darray_find(DArray* thiz, DataCompareFunc cmp, void* ctx)
{
	size_t i = 0;
	return_val_if_fail(thiz != NULL && cmp != NULL, -1);

	/* 遍历动态数组 */
	for(i = 0; i < thiz->size; i++)
	{
		if(cmp(ctx, thiz->data[i]) == 0)
		{
			break;
		}
	}

	return i;
}

  调用者可根据需要对数据进行比较(使用函数指针)返回指定数据的动态数组下标。

三、动态数组源码及函数功能测试

darray.c

#include <stdlib.h>
#include "darray.h"

#define MIN_PRE_ALLOCATE_NR 10
/**
 * @struct:	动态数组结构体
*/
struct _DArray
{
	void** data;					//数据
	size_t size;					//实际使用数组大小
	size_t alloc_size;				//已分配数组大小

	void* data_destroy_ctx;			//销毁函数中上下文变量
	DataDestroyFunc data_destroy;	//销毁函数指针,为指向销毁函数
};

/**
 * @function: 动态数组 数据销毁函数
 * 
 * @param thiz:		动态数组对象
 * @param data:		数据
 * 
 * @return: 无
*/
static void darray_destroy_data(DArray* thiz, void* data)
{
	if(thiz->data_destroy != NULL)
	{
		thiz->data_destroy(thiz->data_destroy_ctx, data);
	}

	return;
}

/**
 * @function:动态数组 创建函数
 * 
 * @param data_destroy: 使用者编写的销毁函数的函数指针
 * @param ctx: 			销毁函数中的上下文数据
 * 
 * @return:				动态数组对象
 * @description:
 * 		开辟一块动态数组结构体大小的空间,并赋初始值。
*/
DArray* darray_create(DataDestroyFunc data_destroy, void* ctx)
{
	DArray* thiz = malloc(sizeof(DArray));

	if(thiz != NULL)
	{

		thiz->data  = NULL;
		thiz->size  = 0;
		thiz->alloc_size = 0;
		thiz->data_destroy = data_destroy;
		thiz->data_destroy_ctx = ctx;
	}

	return thiz;
}


/**
 * @function: 动态数组 扩大存储空间函数
 * 
 * @param thiz: 动态数组对象
 * @param need: 所需动态数组大小
 * 
 * @return: 执行状态
 * @description:
 * 		扩展数组时,并不是一次扩展一个元素,而是多个元素,1.5倍为作者的经验所得
*/
static Ret darray_expand(DArray* thiz, size_t need)
{
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	/* 已分配动态数组大小不够,重新分配动态数组大小 */
	if((thiz->size + need) > thiz->alloc_size)	
	{
		size_t alloc_size = thiz->alloc_size + (thiz->alloc_size>>1) + MIN_PRE_ALLOCATE_NR;

		void** data = (void**)realloc(thiz->data, sizeof(void*) * alloc_size);
		if(data != NULL)
		{
			thiz->data = data;
			thiz->alloc_size = alloc_size;
		}
	}

	return ((thiz->size + need) <= thiz->alloc_size) ? RET_OK : RET_FAIL;
}

/**
 * @function: 动态数组 缩小存储空间函数
 * 
 * @param thiz:动态数组对象
 * 
 * @return:执行状态
 * @description:
 * 当  实际空间大小的一半 还大于当前数组大小 且 实际空间大小 要大于 固定值 (预防为0的情况)时,减小为有效空间的1.5倍。
 * 注:删除元素时也并不是马上释放空间,而是等空闲空间高于某个值才释放它们。
 * 
*/
static Ret darray_shrink(DArray* thiz)
{
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	/* 已分配数组空间大小太大 */
	if((thiz->size < (thiz->alloc_size >> 1)) && (thiz->alloc_size > MIN_PRE_ALLOCATE_NR))
	{
		size_t alloc_size = thiz->size + (thiz->size >> 1);

		void** data = (void**)realloc(thiz->data, sizeof(void*) * alloc_size);
		if(data != NULL)
		{
			thiz->data = data;
			thiz->alloc_size = alloc_size;
		}
	}

	return RET_OK;
}

/**
 * @function: 动态数组 插入函数
 * 
 * @param thiz:  动态数组对象
 * @param index: 要插入的下标位置
 * @param data:  要插入的数据
 * 
 * @return : 执行状态
 * 
 * @description:
 * 	 注意:数组是从0开始的,故for循环中thiz->size无需+1
 * 		 但thiz->size表示的是动态数组当前数量,即之后必须加1
*/
Ret darray_insert(DArray* thiz, size_t index, void* data)
{
	Ret ret = RET_OOM;
	size_t cursor = index;
	return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS); 

	cursor = cursor < thiz->size ? cursor : thiz->size;

	if(darray_expand(thiz, 1) == RET_OK)
	{
		size_t i = 0;

		/* 将要插入位置以后的数据 向后移动一个单元 */
		for(i = thiz->size; i > cursor; i--)		
		{
			thiz->data[i] = thiz->data[i-1];
		}

		thiz->data[cursor] = data;
		thiz->size++;							
		
		ret = RET_OK;
	}

	return ret;
}

/**
 * @function: 动态数组  插入函数(头插法)
 * 
 * @param thiz:动态数组对象
 * @param data:数据内容
 * 
 * @return: 执行状态
*/
Ret darray_prepend(DArray* thiz, void* data)
{
	return darray_insert(thiz, 0, data);
}

/**
 * @function:动态数组 插入函数(尾插法)
 * 
 * @param thiz:动态数组
 * @param data:数据内容
 * 
 * @return: 执行状态
 * @description:
 * 		因为size_t 为无符号整数类型 故数值为最大
*/
Ret darray_append(DArray* thiz, void* data)
{
	return darray_insert(thiz, -1, data);
}

/**
 * @function:动态数组 删除函数
 * 
 * @param thiz: 动态数组对象
 * @param index:下标数值
 * 
 * @return: 执行状态
*/
Ret darray_delete(DArray* thiz, size_t index)
{
	return_val_if_fail(thiz != NULL && thiz->size > index, RET_INVALID_PARAMS); 
	size_t i = 0;
	Ret ret = RET_OK;

	/* 删除指定节点数据 */
	darray_destroy_data(thiz, thiz->data[index]);			

	/* 数据前移 */
	for(i = index; (i+1) < thiz->size; i++)
	{
		thiz->data[i] = thiz->data[i+1];
	}

	thiz->size--;			//当前数量-1
	darray_shrink(thiz);	//检查是否需要缩小内存空间

	return RET_OK;
}

/**
 * @function:动态数组  获取指定下标数据函数
 * 
 * @param thiz:  动态数组
 * @param index: 下标
 * @param data:	 数据
 * 
 * @return: 执行状态
 * 
 * @description:
 * 		int  data   		数据  
 * 		void *data  	data数据的指针
 * 		void **data 	data数据的指针的指针 
 * 
 * 		int data ;  第三个参数传入:(void**)&data)
 * 		&data为地址,强制转换为(void**)
 * 
*/
Ret darray_get_by_index(DArray* thiz, size_t index, void** data)
{
	return_val_if_fail(thiz != NULL && data != NULL && index < thiz->size, 
		RET_INVALID_PARAMS); 

	*data = thiz->data[index];

	return RET_OK;
}

/**
 * @function:动态数组 设置指定下标数据函数
 * 
 * @param thiz:  动态数组对象
 * @param index: 下标
 * @param data:	 数据
 * 
 * @return: 执行状态
*/
Ret darray_set_by_index(DArray* thiz, size_t index, void* data)
{
	return_val_if_fail(thiz != NULL && index < thiz->size, 
		RET_INVALID_PARAMS); 

	thiz->data[index] = data;

	return RET_OK;
}

/**
 * @function:动态数组 获取数组长度函数
 * 
 * @param thiz:动态数组对象
 * 
 * @return: 当前动态数组的长度
*/
size_t   darray_length(DArray* thiz)
{
	size_t length = 0;
	
	return_val_if_fail(thiz != NULL, 0);

	return thiz->size;
}

/**
 * @function:动态数组 遍历函数
 * 
 * @param thiz: 动态数组对象
 * @param visit:回调函数
 * @param ctx:	回调函数上下文变量
 * 
 * @return: 当前动态数组的长度
*/
Ret darray_foreach(DArray* thiz, DataVisitFunc visit, void* ctx)
{
	return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
	size_t i = 0;	
	Ret ret = RET_OK;
	
	/* 遍历动态数组 */
	for(i = 0; i < thiz->size; i++)
	{
		ret = visit(ctx, thiz->data[i]);
	}

	return ret;
}

/**
 * @function:动态数组 查找函数
 * 
 * @param thiz: 动态数组对象
 * @param cmp:  回调函数
 * @param ctx:	回调函数上下文变量(要查找的数值)
 * 
 * @return: 对应数据的下标
 * 
 * @desciption:
 * 		遍历动态数组,查找动态数组中存不存在数据(ctx),若找到则直接跳出并返回对应下标
*/
int darray_find(DArray* thiz, DataCompareFunc cmp, void* ctx)
{
	size_t i = 0;
	return_val_if_fail(thiz != NULL && cmp != NULL, -1);

	/* 遍历动态数组 */
	for(i = 0; i < thiz->size; i++)
	{
		if(cmp(ctx, thiz->data[i]) == 0)
		{
			break;
		}
	}

	return i;
}

/**
 * @function:动态数组 销毁函数
 * 
 * @param thiz: 动态数组对象
 * 
 * @return: 无
*/
void darray_destroy(DArray* thiz)
{
	size_t i = 0;
	
	if(thiz != NULL)
	{
		for(i = 0; i < thiz->size; i++)
		{
			darray_destroy_data(thiz, thiz->data[i]);
		}
		
		SAFE_FREE(thiz->data);
		SAFE_FREE(thiz);
	}

	return;
}


darray.h

#include <stdio.h>
#include "typedef.h"

#ifndef DARRAY_H
#define DARRAY_H

DECLS_BEGIN

struct _DArray;
typedef struct _DArray DArray;

DArray* darray_create(DataDestroyFunc data_destroy, void* ctx);         //创建函数

Ret    darray_insert(DArray* thiz, size_t index, void* data);           //插入函数
Ret    darray_prepend(DArray* thiz, void* data);                        //插入函数(头插法)
Ret    darray_append(DArray* thiz, void* data);                         //插入函数(尾插法)
Ret    darray_delete(DArray* thiz, size_t index);                       //删除函数
Ret    darray_get_by_index(DArray* thiz, size_t index, void** data);    //获取指定下标数据函数
Ret    darray_set_by_index(DArray* thiz, size_t index, void* data);     //设置指定下标数据函数
size_t darray_length(DArray* thiz);                                     //获取数组长度函数
int    darray_find(DArray* thiz, DataCompareFunc cmp, void* ctx);       //查找函数
Ret    darray_foreach(DArray* thiz, DataVisitFunc visit, void* ctx);    //遍历函数

void darray_destroy(DArray* thiz);                                      //销毁函数

DECLS_END

#endif/*DARRAY_H*/


typedef.h

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#ifndef TYPEDEF_H
#define TYPEDEF_H

/**
 * @description:
 * 		返回值枚举类型
*/
typedef enum _Ret
{
	RET_OK,					//成功
	RET_OOM,				//内存溢出
	RET_STOP,				//暂停
	RET_INVALID_PARAMS,		//无效参数
	RET_FAIL				//失败
}Ret;

typedef void     (*DataDestroyFunc)(void* ctx, void* data);
typedef int      (*DataCompareFunc)(void* ctx, void* data);
typedef Ret      (*DataVisitFunc)(void* ctx, void* data);

#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END   }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/

#define return_if_fail(p) if(!(p)) \
	{printf("%s:%d Warning: "#p" failed.\n", \
		__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
	{printf("%s:%d Warning: "#p" failed.\n",\
	__func__, __LINE__); return (ret);}

#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}

typedef Ret (*SortFunc)(void** array, size_t nr, DataCompareFunc cmp);

#endif/*TYPEDEF_H*/


main.c


#include <stdlib.h>
#include <assert.h>
#include "darray.h"

/**
 * @function: 比较函数
 * 
 * @param ctx: 上下文变量,要查找的数据
 * @param data:动态数组的数据
 * 
 * @return:返回动态数组遍历的数据与当前想要查找数据之间的差值
*/
static int int_cmp(void* ctx, void* data)
{
	return (int)data - (int)ctx;
}

/**
 * @function: 打印函数
 * 
 * @param ctx: 上下文变量
 * @param data:动态数组的数据
 * 
 * @return:执行状态
 * 
 * @description:
 * 		打印动态数组的数据
*/
static Ret print_int(void* ctx, void* data)
{
	printf("%d ", (int)data);

	return RET_OK;
}

/**
 * @function: 检测
 * 
 * @param ctx: 上下文变量,要检查并减小的数据
 * @param data:动态数组的数据 
 * 
 * @return:执行状态
 * 
 * @description:
*/
static Ret check_and_dec_int(void* ctx, void* data)
{
	int* expected =(int*)ctx;
	assert(*expected == (int)data);

	(*expected)--;

	return RET_OK;
}
#ifdef  DARRAY_TEST
/**
 * @function: 测试动态数组
 * 
 * @param:  无
 * 
 * @return: 无
*/
static void test_int_darray(void)
{
	int i = 0;
	int n = 10;
	int data = 00;
	DArray* darray = darray_create(NULL, NULL);

	for(i = 0; i < n; i++)
	{
		assert(darray_append(darray, (void*)i) == RET_OK);					//插入(尾插法)
		assert(darray_length(darray) == (i + 1));							//获取长度,判断长度正不正常
		assert(darray_get_by_index(darray, i, (void**)&data) == RET_OK);	//获取数据
		assert(data == i);
		assert(darray_set_by_index(darray, i, (void*)(2*i)) == RET_OK);	    //设置数据 数据x2
		assert(darray_get_by_index(darray, i, (void**)&data) == RET_OK);	//获取数据
		assert(data == 2*i);
		assert(darray_set_by_index(darray, i, (void*)i) == RET_OK);			//设置数据 设置回去
		assert(darray_find(darray, int_cmp, (void*)i) == i);				//比较动态数组中存不存在i
	}

	for(i = 0; i < n; i++)
	{
		assert(darray_get_by_index(darray, 0, (void**)&data) == RET_OK);	//获取数据
		assert(data == (i));
		assert(darray_length(darray) == (n-i));								//获取长度,判断长度正不正常
		assert(darray_delete(darray, 0) == RET_OK);							//删除动态数组第一个数据
		assert(darray_length(darray) == (n-i-1));							//获取长度,判断长度正不正常
		if((i + 1) < n)														
		{
			assert(darray_get_by_index(darray, 0, (void**)&data) == RET_OK);//获取数据
			assert((int)data == (i+1));
		}
	}

	assert(darray_length(darray) == 0);										//获取长度,判断长度正不正常,前面有没有删除干净

	for(i = 0; i < n; i++)
	{
		assert(darray_prepend(darray, (void*)i) == RET_OK);					//插入(头插法)	
		assert(darray_length(darray) == (i + 1));							//获取长度,判断长度正不正常	
		assert(darray_get_by_index(darray, 0, (void**)&data) == RET_OK);	//获取数据	
		assert(data == i);
		assert(darray_set_by_index(darray, 0, (void*)(2*i)) == RET_OK);		//设置数据 数据x2
		assert(darray_get_by_index(darray, 0, (void**)&data) == RET_OK);	//获取数据
		assert(data == 2*i);
		assert(darray_set_by_index(darray, 0, (void*)i) == RET_OK);			//设置数据 设置回去
	}
	
	i = n - 1;
	assert(darray_foreach(darray, check_and_dec_int, &i) == RET_OK);		

	darray_destroy(darray);

	return;
}

/**
 * @function: 测试无效参数报错
 * 
 * @param  : 无 
 * 
 * @return : 无
*/
static void test_invalid_params(void)
{
	printf("===========Warning is normal begin==============\n");
	assert(darray_length(NULL) == 0);
	assert(darray_prepend(NULL, 0) == RET_INVALID_PARAMS);
	assert(darray_append(NULL, 0) == RET_INVALID_PARAMS);
	assert(darray_delete(NULL, 0) == RET_INVALID_PARAMS);
	assert(darray_insert(NULL, 0, 0) == RET_INVALID_PARAMS);
	assert(darray_set_by_index(NULL, 0, 0) == RET_INVALID_PARAMS);
	assert(darray_get_by_index(NULL, 0, NULL) == RET_INVALID_PARAMS);
	assert(darray_find(NULL, NULL, NULL) < 0);
	assert(darray_foreach(NULL, NULL, NULL) == RET_INVALID_PARAMS);
	printf("===========Warning is normal end==============\n");

	return;
}

static void single_thread_test(void)
{
	test_int_darray();
	test_invalid_params();

	return;
}
#endif

int main(int argc, char* argv[])
{
	int i = 0;
	int n = 10;
	int data = 0;

#ifdef  DARRAY_TEST
	single_thread_test();
#endif
	DArray* darray = darray_create(NULL, NULL);

	//尾插法对动态数组赋值,构建动态数组
	for(i = 0; i < n; i++)
	{
		darray_append(darray, (void*)i);					//插入(尾插法)
	}
	darray_foreach(darray,print_int,NULL);							//输出动态数组内容
	printf("\n");
	darray_get_by_index(darray, 3, (void**)&data);					//获取下标3的数据,存入data遍历
	printf("%d\n",data);											//将获取到的数据进行输出查看
	darray_set_by_index(darray, 3, (void*)(6));						//将下标3的数据变为6
	darray_foreach(darray,print_int,NULL);							//输出动态数组内容		
	printf("\n");
	printf("%d\n",darray_find(darray, int_cmp, (void*)6));			//查找数据6的下标
	printf("%d\n",darray_length(darray));							//输出动态数组长度
    darray_destroy(darray);											
	return 0;
}

Makefile

CFILES=darray.c main.c
all:

	gcc -m32 -g -DDARRAY_TEST  $(CFILES)  -o darray_test

clean:
	rm -f *test 

测试结果:

===========Warning is normal begin==============
darray_length:280 Warning: thiz != NULL failed.
darray_insert:142 Warning: thiz != NULL failed.
darray_insert:142 Warning: thiz != NULL failed.
darray_delete:203 Warning: thiz != NULL && thiz->size > index failed.
darray_insert:142 Warning: thiz != NULL failed.
darray_set_by_index:262 Warning: thiz != NULL && index < thiz->size failed.
darray_get_by_index:243 Warning: thiz != NULL && data != NULL && index < thiz->size failed.
darray_find:324 Warning: thiz != NULL && cmp != NULL failed.
darray_foreach:296 Warning: thiz != NULL && visit != NULL failed.
===========Warning is normal end==============
hello0 1 2 3 4 5 6 7 8 9 
3
0 1 2 6 4 5 6 7 8 9 
3
10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值