最近在CSDN上看到了absurd大神的几篇关于系统程序员成长计划的的博文
里面提到了关于通用链表实现的思想,虽然数据结构学的还行,但是真的没写过通用的链表,对封装的认识比较浅显!
于是乎决定实现一下,真正开始写才发现,对我这么个眼高手低的菜鸟来说挺有难度的。写篇博文记录下。
整体实现的主题思想还是absurd大神博文中的两个重要思想:
1, 链表数据段存指针void*
2 采用回调函数来使的链表的基本函数更有通用性
贴代码:
links.h
#ifndef LINKS_H_
#define LINKS_H_
#include
#include
#include
#define LEN_LIST sizeof(DListNode)
typedef bool (*DListDataPrintFunc)(void* data); //打印链表的回调函数接口
typedef bool (*DListVistFunc)(void* data, void* ctx); //遍利链表的回掉函数接口
typedef bool (*DListDataAssign)(void* data, void* ctx);
typedef struct _DListNode{
struct _DListNode* prev;
struct _DListNode* next;
void * data;
//int data;
}DListNode,*ptrDListNode;
typedef struct {
ptrDListNode head,tail;
int len;
}LinkList,*ptrLinkList;
//开辟一个节点 返回指向该节点的指针
ptrDListNode CreateNode();
//释放一个节点内存 返回空指针值
ptrDListNode DestroyDListNode(ptrDListNode e);
//初始化一个链表头节点
ptrLinkList InitList();
//销毁一个链表头节点 以及链表内所有元素 返回NULL
ptrLinkList DestroyLinkList(ptrLinkList L);
//为链表的节点赋值
bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx);
//在第n个节点后插入s指向的节点
bool InsDListNode(ptrLinkList L, ptrDListNode s, int n);
//删除第n个节点 用q返回
bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n);
//打印链表
void links_print(ptrLinkList L, DListDataPrintFunc);
//遍历链表 根据回调函数 用ctx返回所求结果
bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx);
#endif//
links.c
#include "links.h"
//给节点data赋值
bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx)
{
assign(data,ctx);
}
//开辟一个结点
ptrDListNode CreateNode(int size_data)
{
ptrDListNode p = (ptrDListNode) malloc(LEN_LIST);
p->prev = NULL;
p->next = NULL;
p->data = (void*) malloc(size_data);
return p;
}
//初始化头节点
ptrLinkList InitList()
{
ptrLinkList L = (ptrLinkList) malloc(sizeof(LinkList));
L->head = NULL;
L->tail = NULL;
L->len = 0;
return L;
}
//销毁一个节点
ptrDListNode DestroyDListNode(ptrDListNode e)
{
free(e->data);
free(e);
e = NULL;
return NULL;
}
//销毁整个链表
ptrLinkList DestroyLinkList(ptrLinkList L)
{
ptrDListNode p = L->head;
int n = L->len;
while(n--)
{
p = p->next;
DestroyDListNode(L->head);
L->head = p;
}
free(L);
return NULL;
}
//在第一个节点前插入
static void InsFirst(ptrLinkList L, ptrDListNode s)
{
ptrDListNode p = L->head;
L->head = s;
s->prev = NULL;
if(L->len == 0)
{
L->tail = s;
s->next = NULL;
}
else{
s->next = p;
p->prev = s;
}
L->len++;
}
//在第n个节点后插入
bool InsDListNode(ptrLinkList L, ptrDListNode s, int n)
{
if(n > L->len || n < 0 ) return false;
if(!n){
InsFirst(L,s);
return true;
}
ptrDListNode p = L->head;
if(n == L->len) L->tail = s;
while(--n)
{
p = p->next;
}
s->next = p->next;
p->next = s;
s->prev = p;
if(L->tail != s)
s->next->prev = s;
else s->next = NULL;
L->len ++;
return true;
}
//删除第一个节点q返回
static bool DelFirst(ptrLinkList L, ptrDListNode* q)
{
if(L->len == 0) return false;
*q = L->head;
if(L->len == 1){
L->head = NULL;
L->tail = NULL;
}
else
{
L->head = L->head->next;
L->head->prev = NULL;
}
(*q)->next = NULL;
L->len--;
return true;
}
//删除第n个节点 q你返回
bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n)
{
if(n > L->len || n < 0) return false;
if(!n)
{
DelFirst(L,q);
return true;
}
ptrDListNode p = L->head;
while(--n)
{
p = p->next;
}
if(p == L->tail){
L->tail = p->prev;
}
else
p->next->prev = p->prev;
p->prev->next = p->next;
*q = p;
(*q)->next = NULL;
(*q)->prev = NULL;
L->len--;
return true;
}
//遍历访问
bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx)
{
ptrDListNode p = L->head;
int n = L->len;
long long* ctx_temp = ctx;
*ctx_temp = 0;
while(n--)
{
visit(p->data, ctx);
p = p->next;
}
return true;
}
//遍历打印
void links_print(ptrLinkList L, DListDataPrintFunc print)
{
ptrDListNode p = L->head;
int n = L->len;
while(n--)
{
print(p->data);
putchar('\n');
p = p->next;
}
}
test.c
#include
#include "links.h"
#define DATA_SIZE sizeof(Node)
typedef struct Node{
int a;
int b;
}Node,*ptrNode;
static bool Data_Assign(void* data, ptrNode ctx)
{
ptrNode temp = data;
temp->a = ctx->a;
temp->b = ctx->b;
return true;
}
static bool print_int(void* data)
{
ptrNode temp = data;
printf("%d %d",temp->a, temp->b);
return true;
}
static bool get_sum(void* data, void* ctx)
{
ptrNode temp = data;
long long* result = ctx;
*result += temp->b;
return true;
}
static bool find_max(void* data, void* ctx)
{
ptrNode temp = data;
int* pimax = ctx;
if(*pimax < temp->a) *pimax = temp->a;
return true;
}
int main()
{
int i;
ptrLinkList p = InitList();
ptrDListNode s = CreateNode(DATA_SIZE);
ptrDListNode s1 = CreateNode(DATA_SIZE);
ptrDListNode s2 = CreateNode(DATA_SIZE);
ptrDListNode s3 = CreateNode(DATA_SIZE);
ptrDListNode s4;
long long sum;
int imax;
Node ss[5];
for(i = 1; i<5; i++)
{
ss[i].a = i;
ss[i].b = i*10 + i;
}
DListNodeGetData(s->data, Data_Assign, &ss[1]);
DListNodeGetData(s1->data, Data_Assign, &ss[2]);
DListNodeGetData(s2->data, Data_Assign, &ss[3]);
DListNodeGetData(s3->data, Data_Assign, &ss[4]);
InsDListNode(p,s,0);
InsDListNode(p,s1,0);
InsDListNode(p,s2,0);
InsDListNode(p,s3,3);
links_print(p,print_int);
Dlist_Foreach(p, get_sum, &sum);
Dlist_Foreach(p, find_max, &imax);
printf("%lld %d\n",sum,imax);
DestroyLinkList(p);
return 0;
}
毕竟绝大多数的链表数据段都是存储的结构体,真正存储结构体的时候又涉及到void型指针进行内存分配的问题,这里依旧需要用回调函数来完成,毕竟内部函数不知道到底是怎样的一个结构体。
整体代码测试通过,由于在linux下编写的,gdb调试用的不熟练,效率太低,而且使用makefile之后我都不会用gdb调试了,list都不出代码了,脑袋完全大了。这次也是人生第一次编写makefile文件。贴出来吧,以后回来改,估计是makefile写的有问题才整的gdb调试的时候只能run不能list....
makefile
test:test.o links.o
gcc test.o links.o -o test
test.o:test.c links.h
gcc -c -g test.c
links.o:links.c links.h
gcc -c -g links.c
#this is a makefile