线性表Arrary List也就是基于数组实现的表结构,由于基于数组实现,所以其特点非常明显:随机访问(访问指定序号的节点)时间开销O(1),插入删除(在当前位置插入删除)时间开销O(N)。
表结构的操作主要有创建、插入(头、尾、当前位置)、删除(头、尾、当前位置)、查找(指定键值、指定序号)和游走(指向头、尾、上一个节点、下一个节点),一般来讲基于数组的表结构都是预先指定最大容量,这样使用起来会比较不方便,所以可以利用realloc实现表结构的扩容。
Arrary List实现的代码可以参见附件(辅助异常检查CheckError.h,头文件AList.h,代码文件AList.c,模块功能测试ListDemo.c),下面主要讨论实现过程中的一些问题:
1.从程序稳定性的角度,必须对可能引起程序崩溃的函数参数以及状态进行检查,所以除了计算指定最大节点数量计算所需内存字节数的ALIST_get_size直接返回int,其他各函数均返回HRESULT状态码,然后在外部调用后应当检查hr值是否为RET_S_OK,以便确认函数调用成功,当然ListDemo仅是对函数功能是否正常进行验证以及演示如何使用,这些检查没有加入。PS,如果是声明为static的内部函数,由于进入其他函数已经检查状态,并且如果调用这些函数时可以保证不会出现异常,个人认为可以省略这些检查。
这样的做法与有一些库函数直接使用NULL,-1等值表示调用失败应该是更好一些(通过查看hr的具体值可以知道具体的错误原因)。
2.ALIST的定义如下:
typedef struct tagALIST
{
int max_count; // 最大长度
int used_count; // 已经使用长度
int curr; // 当前节点的位置
ALIST_NODE nodes[1]; // ALIST的第一个节点
} ALIST;
这里nodes声明为长度为1的数组而非指针,其好处在于配合get_size或者直接create函数一次直接分配ALIST以及表的节点所需的内存,避免了二次申请内存或者将指针指向内存位置的操作(如一次分配LIST+NODES的内存,再将nodes指向LIST结束后的第一个位置),该思路源于gdi中颜色调板的数据结构设计。同时,这样做也可以防止创建长度为0的表结构导致head和tail无效。
3.CHECK_REALLOC的宏定义如下
{ \
void *tmp; \
tmp = (type)realloc(ptr, new_size); \
CHECK_ERROR_HR(NULL == tmp, \
ERR_RET_MEM_ALLOC, \
ERR_MSG_MEM_ALLOC); \
ptr = tmp; \
}
使用tmp临时指针避免realloc失败(内存不足,不能够分配指定长度的内存,realloc保持原先的内存分配并返回NULL)导致原指针值为NULL而导致原先的内存泄漏。
BSKER于2011.11.06