一、场景
-
C语言编码,经常需要动态数组处理的情况,比如,我们需要处理一段字符串。现在要将字符串切割返回到 一个列表中。
/* 比如字符串: name=renshaoxia&number=66666&p=890&fjdkf=ddd&dfsjfkd=dd... 我希望把这个字符串使用&符号分隔开。而后随时找到任何一个我要的元素。 如果我们有个动态数组的的库,如果能够自动把数据弄成如下格式: arr[0] -> "name=renshaoxia" arr[1] -> "number=6666" arr[2] -> "p=809" ... 瞬间就觉得很好处理了。 */
-
使用C语言的库函数切割字符串
/* 比如,使用,切割字符串,如果我实现了 fv_darray_push 函数,就可以轻松的把字符串放到数组中*/ /*假设str_buf的内容是 "1,2,3,4,5,6,7,8,9,10,11,12,,13,14,15,16,17,18,19,20" */ char *str = strtok(str_buf,","); while(str) { /*strdup 分配了空间,需要手动做free*/ fv_darray_push(darray,strdup(str)); str = strtok(NULL,","); } //.....
二、动态数组C语言实现
- 我将动态数组的实现放到了 这里 。
- 其实我也是借鉴了awtk代码的源码。awtk源码里面有排序。虽然会方便,但是我任务排序没必要写到这个模块中。
- 刚开始的时候,我想到的一些API,写的时候发现和自己想的不一样。最后没有实现。正常的编码中也会经常遇到这种情况吧。
- 我写了个例子,分割字符串处理。
三、实现过程中遇到问题记录
-
调试比较浪费时间的是 fv_darray_push 函数。这个函数是扩展当前数组能存放的指针的大小。使用了realloc函数。
/* 1.使用 realloc 函数 参数指定的大小,就是返回指针指向的真正的大小。最开始以为是追加分配的大小。 2.realloc函数,分配的空间,如果是扩大了,那么,之前指针指向空间中的内容不变。超出之前空间的内容没有被初始化。 3.不管空间分配是扩大了还是缩小了。之前的那个指针都被释放了。 4.realloc 传入的指针必须是malloc分配过的。因为内部会做free。 */ ret_t fv_darray_extend(fv_darray_t *darray) { return_value_if_fail(darray , RET_BAD_PARAMS); int size = 8; size = darray->size; darray->elem = realloc(darray->elem,2*size*sizeof(void *)); if(darray->elem == NULL) { darray->size = 0; darray->elem = NULL; return RET_FAIL; } memset(darray->elem+sizeof(void*)*darray->size,0x00,sizeof(void *)*size); darray->size = 2*size; return RET_OK; }
-
我在实现过程中,误以为realloc 是在原来基础上追加空间。所以 一直越界访问。才会一直死机。
-
fv_darray_pop 函数无法实现。因为弹出之后,就必须释放。释放了弹出元素也没有意义。如果弹出元素但不释放,就要用户操作十分的小心。使用也不方便。
-
销毁元素之后,需要将这个指针置空。否则,可能出现double free的死机问题。
四、使用到的技巧整理
- 我将 常用到 的for循环 写成了 for_in_range的宏定义,这可以加速编码速度。
- 我将 常用到的合法性检测,使用return_value_if_fail宏定义,也可以提升编码速度。
- API的前缀可以看出 是属于动态数组模块的API。这样以后使用的时候,方便整理依赖关系。
- 还可以优化的地方:
- 调试打印,现在使用printf,需要优化。
- 内存分配,目前使用calloc,realloc直接分配,需要优化。
- free释放的时候,需要先判断是否为空,以及设置为空的操作。也可以使用宏定义优化。