一个机缘巧合,读了一下《系统程序员成长计划》这本书,感觉应该为此写下自己读后的感觉以及在实现作者所说的功能时所遇到的一些问题。
首先读到的是通用双链表,所谓通用双链表是指能够存储与具体数据类型无关的链表。其中主要的问题是我们如何输出不同的数据类型比如如果是int型用printf("%d");等等其他的 。个人觉得在这里我们首先需要思考什么是封装、为什么要封装、如何实现封装,在C语言中如何实现这些功能。需要了解的概念:C语言中的函数指针、回调函数等等。
函数指针:指向函数的指针变量,,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数.
回调函数:被调者回头调用调用着的函数,这种由调用方自己提供的函数叫回调函数。
有了回调函数和函数指针,我们可以指定调用哪个函数来实现输出int型,哪个函数实现输出string类型等等,这样在运行时由系统决定调用哪种函数。说了输出,再来说下如何存储不同的数据呢?其实很容易可以想到void* 。
以下是参照作者所写的代码在vs2010下可以运行的代码。代码的组成是dlist.h、dlist.c、main.c:
首先是dlist.h,代码如下:
/* 练习写一个通用的双链表 */
#ifndef DLIST_H
#define DLIST_H#ifdef _cplusplus /* 使用C++编译C程序,一般的写法是添加关键字 extern */
extern "C" {
#endiftypedef enum _DLIST_RET /* 定义函数返回值的枚举类型 */
{
DLIST_RET_OK, /*成功的返回值:0 */
DLIST_RET_OOM, //
DLIST_RET_STOP, /* 异常终止:2 */
DLIST_RET_PARAMS,
DLIST_RET_FAIL /* 操作失败:4 */
}DList_Ret;struct _DList; /* 定义结构体类型 */
typedef struct _DList DList;/* 定义函数指针,DList_Ret函数的返回值,DListDataPrintFuc函数指针的名字,data传递给函数的参数。*/
typedef DList_Ret (*DListDataPrintFuc)(void *Data);/* 声明功能函数 */
DList * DList_create(void); /* 创建双链表 */
DList_Ret DList_insert(DList * thiz,size_t index,void *data); /* 在指定的位置插入数据 */
DList_Ret DList_prepend(DList * thiz,void * data);
DList_Ret DList_append(DList * thiz,void *data); /* 追加到链表 */
DList_Ret DList_delete(DList * thiz,size_t index); /* 删除链表中给定的数据 */
DList_Ret DList_get_by_index(DList * thiz,size_t index,void ** data); /* 检索指定位置的值 */
DList_Ret DList_set_by_index(DList * thiz,size_t index,void **data); /* 设置指定位置的值 */
size_t DList_get_length(DList * thiz); /* 获取链表的长度 */
DList_Ret DList_print(DList * thiz,DListDataPrintFuc print); /* 输出链表值,使用回调函数 */
DList_Ret DList_destroy(DList * thiz); /* 销毁函数 */#ifdef _cplusplus
}
#endif /* C++编译器的定义的结束 */#endif /* 头文件 DLIST_H 结束 */
dlist.c:
/* 通用双链表的具体实现代码 */
#include<stdlib.h>
#include "dlist.h"/* 定义链表借点类型 */
typedef struct _DListNode
{
struct _DListNode * prev; /* 前驱指针 */
struct _DListNode * next; /* 后继指针 */
void *data;/* 因为是通用的双链表,因此无法明确指定数据类型,只有在运行时强制类型转换 */
}DListNode;struct _DList
{
DListNode* first; /* 头指针 */
};/*创建节点函数: 封装的函数,只能本文件中的函数才能调用,保证代码的封装性添加关键字 static */
static DListNode* dlist_node_create(void* data)
{
DListNode* node = (DListNode *) malloc(sizeof(DListNode)); /* 分配空间 */
if(node != NULL)
{
node ->prev = NULL;
node ->next = NULL;
node ->data = data;
}
return node;
}/* 释放节点空间,也是静态函数只能本文件的函数才能使用 */
static void dlist_node_destroy(DListNode* node)
{
if(node != NULL)
{
node ->prev = NULL;
node ->next = NULL;
free(node);
}
return;
}/* 获取指定的节点数据,静态函数 */
static DListNode* dlist_get_node(DList* thiz,int index,int fail_return_last)
{
DListNode* iter = thiz->first; /*头指针*//* 循环读取链表的节点 */
while(iter != NULL && iter->next !=NULL && index > 0)
{
iter = iter->next;
index --;
}if(!fail_return_last)
{
iter = index > 0 ? NULL:iter;
}return iter;
}/* 接口函数的具体实现,在头文件中有声明 */
DList* DList_create(void)
{
DList* thiz =(DList*) malloc(sizeof(DList));if(thiz != NULL)
{
thiz->first = NULL;
}
return thiz;
}
/* 在指定的位置添加信息 */
DList_Ret DList_insert(DList * thiz,size_t index,void *data)
{
DListNode* node = NULL;
DListNode* cursor = NULL;if((node = dlist_node_create(data)) == NULL) /* 创建节点失败 */
{
return DLIST_RET_OOM;
}
if(thiz ->first == NULL) /* 头结点为空 */
{
thiz ->first = node;
return DLIST_RET_OK;
}
cursor = dlist_get_node(thiz,index,1); /* 查找给定索引号的节点 */if(index < DList_get_length(thiz))
{
if(thiz->first == cursor) /* 头结点是给定的节点 */
{
thiz->first = node;
}
else
{
cursor->prev->next = node;
node->prev = cursor->prev;
}
node ->next= cursor;
cursor->prev = node;
}
else /* 将node节点 直接放在cursor后面 */
{
cursor->next = node;
node ->prev = cursor;
}
return DLIST_RET_OK;
}/* 接口函数:直接添加到头结点后面 */
DList_Ret DList_prepend(DList * thiz,void * data)
{
return DList_insert(thiz,0,data); /*调用DList_insert操作 */
}/* 接口函数:追加节点,通过调用insert 操作 */
DList_Ret DList_append(DList* thiz,void* data)
{
return DList_insert(thiz,-2,data);
}/* 接口函数:删除给定索引节点 */
DList_Ret DList_delete(DList* thiz,size_t index)
{
DListNode* cursor = dlist_get_node(thiz,index,0); /*获取给定索引的节点*/
if(cursor != NULL)
{
if(cursor == thiz->first)/* cursor是头结点 */
{
thiz->first = cursor->next;
}
if(cursor->next != NULL) /*cursor 还有后继结点 */
{
cursor->prev->next = cursor->next;
}if(cursor->prev != NULL ) /*cursor还有前驱节点 */
{
cursor ->next->prev = cursor ->prev;
}
dlist_node_destroy(cursor); /* 释放节点信息 */
}return DLIST_RET_OK;
}/* 接口函数:根据索引获取节点的值 */
DList_Ret DList_get_by_index(DList* thiz,size_t index , void** data)
{
DListNode* cursor = dlist_get_node(thiz,index,0);/* 根据索引获得节点信息*/
if(cursor != NULL)
{
*data = cursor->data;
}return cursor != NULL ? DLIST_RET_OK:DLIST_RET_FAIL;
}/* 接口函数:设置给定索引位置的数据 */
DList_Ret DList_set_by_index(DList* thiz,size_t index, void* data)
{
DListNode* cursor = dlist_get_node(thiz,index,0); /* 找到指定的节点 */
if(cursor != NULL)
{
cursor->data = data; /* 给节点的数据域赋值 */
}return cursor != NULL?DLIST_RET_OK:DLIST_RET_FAIL;
}/* 接口函数:去链表的长度 */
size_t DList_get_length(DList* thiz)
{
size_t length = 0;
DListNode* iter = thiz->first;while(iter != NULL)
{
length ++;
iter = iter->next;
}return length;
}/* 接口函数:输出接点的信息 */
DList_Ret DList_print(DList* thiz,DListDataPrintFuc print)
{
DListNode* iter = thiz->first;
while(iter != NULL)
{
print(iter->data);
iter = iter->next;
}return DLIST_RET_OK;
}/* 接口函数:释放链表空间 */
DList_Ret DList_destroy(DList* thiz)
{
DListNode* cursor = thiz->first;
DListNode* next = NULL;while(cursor != NULL)/*循环释放节点的空间*/
{
next = cursor->next;
dlist_node_destroy(cursor);/*释放空间*/
cursor = next;
}thiz->first = NULL;
free(thiz);return DLIST_RET_OK;
}main.c:
/* 测试通用链表 */
#include<stdio.h>
#include<assert.h>
#include"dlist.h"static DList_Ret print_int(void* data)/* 输出整形链表的值 */
{
printf("%d ",(int)data);
return DLIST_RET_OK;
}int main(int argc,char** argv)
{
int i=0;
int n=100;
DList* dlist = DList_create();for(i = 0;i<n;i++)
{
assert(DList_append(dlist,(void*)i) == DLIST_RET_OK);
}
//printf("链表长度是:%d\n",DList_get_length(dlist));
for(i = 0; i<n ;i++)
{
assert(DList_prepend(dlist,(void*)i) == DLIST_RET_OK);
}DList_print(dlist,print_int);
printf("\n");
printf("链表长度是:%d\n",DList_get_length(dlist));
DList_destroy(dlist);
return 0;
}在vs2010中运行以上的程序的结果与预期的结果又出入,希望能够发现问题的能够讨论下。
系统程序员成长计划---读后感(一)通用双链表
最新推荐文章于 2019-10-15 22:22:02 发布