系统程序员成长计划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

 

 

 

自己实现的其他函数(见下一节)。

posted @ 2017-10-28 17:28  为民除害  阅读(198)  评论(0)    收藏  举报