系统程序员成长计划007第三章动态数组
动态数组
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);
根据下面的两个函数,我猜测出动态数组的成员有:
void** data 指向申请到的空间。
这里用二级指针,means 申请空间得到的指针,指向void*类型的数据,即通用数据类型。
但根据下边搬运数据的方法:data[i] = data[i-1],这也单纯是对于一个sizeof(void*)类型的数据进行的操作。 size_t alloc_size 代表已分配的空间大小 size_t size 代表已使用的空间大小
书中透露的动态修改数组大小的两个核心函数:
动态数组的动态性如何实现了呢?其实很简单,借助标准 C 的内存管理函数 realloc,我们可 以轻易改变数组的长度。函数 realloc 是比较费时 的,如果每插入/删除一个元素就要 realloc 一次,不但会带来性能的下降,而且可能造成内存碎片。
为了解决这个问题,需要使用一个 称为预先分配的惯用 手法,预先分配实际上是用空间换时间的典型应用,下面我们看看它 的实现:
/* 扩展空间 在扩展数组时,不是一次扩展一个元素,而是一次扩展多个元素。至于应该扩展多少个,经验数据是扩展为现有元素个数的 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; 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; }
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; } /* 扩展的大小由下列公式得出: size_t alloc_size = (thiz->alloc_size + thiz->alloc_size>>1) + MIN_PRE_ALLOCATE_NR; 计算 1.5*thiz->alloc_size 时,
不使用 1.5 * thiz->alloc_size,因为这样存在浮点数计算, 也不使用 thiz->alloc_size+ thiz->alloc_size/2,如果编译器不做优化,除法指令也是比较慢的操作
这里我们使用(thiz->alloc_size + thiz->alloc_size>>1),这是最快的方法。
后面加上MIN_PRE_ALLOCATE_NR 的原因是避免 thiz->alloc_size 为 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; }
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; } /* 为了避免极端情况下的出现频繁 resize 的情况,在总空间小于等于MIN_PRE_ALLOCATE_NR 时,我们不减少空间的大小。 */
==============================================================================================
gcc支持__func__、__LINE__、__FILE__
VS换名字__FUNCTION__
有关链接:http://www.cnblogs.com/flyingdirt/p/4237539.html
free函数如果记住指针指向空间的大小:http://blog.csdn.net/imxiangzi/article/details/50953858
红框指示的是申请到空间的大小,一个是20(0x14),一个是40(0x28)
绿框指示的是特殊的内存替换(debug模式),这个用来检测内存溢出。都替换为fdfd。【和初始化的值fdfd,没有关系】
free之后
相关的内存被置为dddd dddd
自己实现的其他函数(见下一节)。
联我:shen5773384##163.com