说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《系统程序员成长计划》、C语言实现动态数组,克服静态数组大小固定的缺陷
上一篇:读书笔记 ——《系统程序员成长计划》篇6:写的又快又好的秘诀
下一篇:读书笔记 ——《系统程序员成长计划》篇8:排序算法
目录:
一、概念
1、什么是动态数组?
C语言中,数组长度必须在创建时指定,且只能是常数不能是变量。定义后系统将为它分配固定大小的空间,以后不能改变,称为静态数组。但在编程过程中,有时所需的内存空间无法预先确定,故用静态数组的办法很难解决。
动态数组可随程序需要而重新指定大小。通过执行代码在内存空间从堆(heap)上分配(即动态分配)的。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。
2、双向链表和动态数组的对比
情形 | 动态数组 | 双向链表 |
空间占用情况 | 占用一块连续的内存 | 每个结点占用一块内存 |
频繁增删数据 | 需移动后面元素 | 只需修改前后元素指针,但容易造成内存碎片 |
是否支持多种高效排序算法 | 支持,如:快速排序、归并排序,堆排序等 | 表现不好,甚至不如冒泡排序 |
排序好的数据 | 可使用二分查找 | 仍只能用顺序查找 |
小量数据 | 区别不大 | 区别不大 |
二、动态数据的实现
同样选择存指针,故实现的是指针数组。
1、简单实现
#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;
}
实现现象:
输入数组长度:20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
这边定义了数组指针,指向malloc
函数创建的一整块内存空间,实现动态数组,动态输入数组长度后自动赋值数据并输出。实际上与静态数组并没有什么区别。
2、书中示例 关键函数简述
(1)动态数组 结构体
struct _DArray;
typedef struct _DArray DArray;
typedef void (*DataDestroyFunc)(void* ctx, void* data);
struct _DArray
{
void** data; //数据
size_t size; //实际使用大小
size_t alloc_size; //分配空间大小
void* data_destroy_ctx; //销毁函数中上下文数据
DataDestroyFunc data_destroy; //函数指针,为指向销毁函数
};
(2)动态数组 创建函数
/**
* @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;
}
(3)动态数组 扩展函数
/**
* @function:动态数组 扩展函数
*
* @param thiz: 指向动态数组
* @param need: 所需动态数组大小
*
* @return: 运行状态
* @description:
* 扩展数组时,并不是一次扩展一个元素,而是多个元素,1.5倍为作者的经验所得
*/
#define MIN_PRE_ALLOCATE_NR 10
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;//扩大为原来的1.5倍并加上常数
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; //判断是否执行成功
}
①函数简介:
realloc()
函数:尝试重新调整之前调用 malloc
或 calloc
所分配的ptr
所指向的内存块的大小
函数原型:void *realloc(void *ptr, size_t size)
参数:
1️⃣ ptr
– 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc
、calloc
或 realloc
进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
2️⃣ size
– 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值:
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
②扩展1.5倍的公式:size_t alloc_size = thiz->alloc_size + (thiz->alloc_size>>1) + MIN_PRE_ALLOCATE_NR;
为什么要用这个公式呢?
大多数嵌入式平台并不支持硬件浮点数计算,浮点数的计算要比定点数计算要慢,故并不是直接使用1.5*thiz->alloc_size
.又由于若编译器不做优化,则除法操作也较慢,故也不使用thiz->alloc_size+thiz->alloc_size/2
.
而size_t alloc_size = thiz->alloc_size + (thiz->alloc_size>>1)
则是最快的方法。后面加上MIN_PRE_ALLOCATE_NR
是为避免当thiz->alloc_size
为0时存在的错误。
(4)动态数组 减小函数
/**
* @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))//若现有大小 < 已分配1/2倍 且 已分配空间>最小空间
{
size_t alloc_size = thiz->size + (thiz->size >> 1); //将空间大小缩小为现有的1.5倍
void** data = (void**)realloc(thiz->data, sizeof(void*) * alloc_size);
if(data != NULL)
{
thiz->data = data;
thiz->alloc_size = alloc_size;
}
}
return RET_OK;
}
(5)动态数组 插入函数(包含头插法与尾插法)
/**
* @function:动态数组 插入函数
*
* @param thiz: 指向动态数组
* @param index: 插入的位置
* @param data: 插入的数据
*
* @return: 运行状态
* @description:
*/
Ret darray_insert(DArray* thiz, size_t index, void* data)
{
Ret ret = RET_OOM; //定义返回状态初始值为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) //若成功扩展空间,则将游标以后位置数据后移一个单位后,将指定位置赋值且数组长度变量+1
{
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: 运行状态
* @description:
*/
Ret darray_prepend(DArray* thiz, void* data)
{
return darray_insert(thiz, 0, data);
}
/**
* @function: 尾插法
*
* @param thiz: 指向动态数组
* @param data: 插入的数据
*
* @return: 运行状态
* @description:
*/
Ret darray_append(DArray* thiz, void* data)
{
return darray_insert(thiz, -1, data);
}
(6)动态数组 删除函数
/**
* @function: 动态数组 删除数据
*
* @param thiz: 指向动态数组
* @param data: 数据
*
* @return: 无
* @description:
* 销毁函数在创建时有使用者进行编写
*/
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: 指向动态数组对象
* @param index: 指定下标位置
*
* @return: 执行状态
* @description:
* 删除指定位置数据,数组元素前移。
*/
Ret darray_delete(DArray* thiz, size_t index)
{
size_t i = 0;
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL && thiz->size > index, RET_INVALID_PARAMS); //若动态数组不存在且删除位置大于数组大小
darray_destroy_data(thiz, thiz->data[index]); //删除指定位置数据
for(i = index; (i+1) < thiz->size; i++) //将删除后的数组元素前移
{
thiz->data[i] = thiz->data[i+1];
}
thiz->size--;
darray_shrink(thiz); //检查是否需要减小空间
return RET_OK;
}
(7)动态数组 销毁函数
/**
* @function: 动态数组 销毁函数
*
* @param thiz: 指向动态数组
*
* @return: 无
* @description:
* 删除所有数据,销毁动态数组,销毁动态数组空间
*/
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;
}
3.书中程序
typedef.h
/*
* File: typedef.h
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: common types definition.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2008-12-10 Li XianJing <xianjimli@hotmail.com> created.
*
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
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*/
darray.h
/*
* File: darray.h
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: dynamic array implementation.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2009-01-02 Li XianJing <xianjimli@hotmail.com> created
*/
#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*/
darray.c
/*
* File: darray.c
* Author: Li XianJing <xianjimli@hotmail.com>
* Brief: dynamic array implementation.
*
* Copyright (c) Li XianJing
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* History:
* ================================================================
* 2009-01-02 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include <stdlib.h>
#include "darray.h"
struct _DArray
{
void** data;
size_t size;
size_t alloc_size;
void* data_destroy_ctx;
DataDestroyFunc data_destroy;
};
static void darray_destroy_data(DArray* thiz, void* data)
{
if(thiz->data_destroy != NULL)
{
thiz->data_destroy(thiz->data_destroy_ctx, data);
}
return;
}
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;
}
#define MIN_PRE_ALLOCATE_NR 10
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;
}
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;
}
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;
}
Ret darray_prepend(DArray* thiz, void* data)
{
return darray_insert(thiz, 0, data);
}
Ret darray_append(DArray* thiz, void* data)
{
return darray_insert(thiz, -1, data);
}
Ret darray_delete(DArray* thiz, size_t index)
{
size_t i = 0;
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL && thiz->size > index, RET_INVALID_PARAMS);
darray_destroy_data(thiz, thiz->data[index]);
for(i = index; (i+1) < thiz->size; i++)
{
thiz->data[i] = thiz->data[i+1];
}
thiz->size--;
darray_shrink(thiz);
return RET_OK;
}
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;
}
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;
}
size_t darray_length(DArray* thiz)
{
size_t length = 0;
return_val_if_fail(thiz != NULL, 0);
return thiz->size;
}
Ret darray_foreach(DArray* thiz, DataVisitFunc visit, void* ctx)
{
size_t i = 0;
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
for(i = 0; i < thiz->size; i++)
{
ret = visit(ctx, thiz->data[i]);
}
return ret;
}
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;
}
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;
}
#ifdef DARRAY_TEST
#include <assert.h>
static int int_cmp(void* ctx, void* data)
{
return (int)data - (int)ctx;
}
static Ret print_int(void* ctx, void* data)
{
printf("%d ", (int)data);
return RET_OK;
}
static Ret check_and_dec_int(void* ctx, void* data)
{
int* expected =(int*)ctx;
assert(*expected == (int)data);
(*expected)--;
return RET_OK;
}
static void test_int_darray(void)
{
int i = 0;
int n = 100;
int data = 0;
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);
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);
}
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);
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;
}
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;
}
int main(int argc, char* argv[])
{
single_thread_test();
return 0;
}
#endif
Makefile
CFILES=darray.c
all:
gcc -g -shared -lpthread $(CFILES) -o libdarray.so
gcc -g -DDARRAY_TEST -lpthread $(CFILES) -o darray_test
clean:
rm -f *test *.exe *.so