一,前言
从今天开始我准备进行一轮数据结构相关的C语言设计复习。原因是看代码的时候发现数据结构每个工程都有,然后都用自己的方式进行了封装,做成基础API供调用。而我平时很少去造轮子,但是最近看了linux+qemu+littlevgl+nuttxOS源码后,发现他们关于链表的具体设计的都不太一样,但是总体的抽象封装做的都很好。另外,他们的代码结构分层做的都很好,移植性和模块可扩展性都很好。为了将来也做一个开源大型工程,参考优秀开源代码的设计思路后,我现在要开始准备自己的轮子了。学以致用乐趣无穷。
二,代码框架
当前我用的是比较简单的输入输出系统,一个main中循环等待输入的待运行的example号,然后通过列表跳转法进入example子系统。example子系统中可以添加各个独立的小的example,比如我已经添加了2个单链表的example,main函数的列表中若添加了example子系统中的函数,则输入数字后,可以运行。这样的example子系统配置方式我是参考了littlevgl的设计,感觉不错,example文件夹可以不断的扩展c代码,只要添加到main.c数组testExample_st中就可以了。然后就是copy了lvgl中的log系统,就是一个c和一个h文件,做debug输出用。框架图如下,example子自动中可以不断扩展,今天做的是2个单链表的例子。
main.c源码
/******************************************************************************
Project : MiniC
Description : MiniC project
CPU and Compiler : win10 codeblocks mingw32
Author : AppleCai
Date : 2020-04-23
*******************************************************************************/
/*********************
* INCLUDES
*********************/
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL VAR
**********************/
/**********************
* CONST VAR
**********************/
const example testExample_st[NUM_OF_TESTEXAMPLE]=
{
{'1',slist}, /* test1.c */
{'2',slist2}, /* test2.c */
};
/**********************
* Functions Implement
**********************/
int main()
{
char cmd;
int i = 0;
unsigned char found=0;
printf("**************************************************************\n");
printf("****Welcome to miniC V1.0.0 project! Copyright by AppleCai****\n");
printf("**************************************************************\n");
printf("Please select test item[1-9],print q to exit!Please:");
while (scanf("%c",&cmd))
{
getchar(); //clear \n
if (cmd=='q')
{
printf("Bye Apple Cai!\n");
break;
}
else
{
found = 0;
for(i=0; i<NUM_OF_TESTEXAMPLE; i++)
{
/* If match,then run the example code */
if(cmd == testExample_st[i].sn)
{
found = 1;
testExample_st[i].RunExample();
}
}
/* If not found CMD in list, print a message */
if(!found)
{
printf("Please select a valid test item for 1 to 9,print q to exit!Please:");
}
}
}
printf("Please press any key to exit the miniC project!");
getchar();
return 0;
}
三,test1中精华说明
我花费时间看源码除了觉得好奇或者好玩外,我顺便还取其精华去其糟粕。那么让我印象深刻的就是可复用性,因为我看的代码中双链表都抽象的很好,但是我今天先做下单链表。单链表其实不怎么用的,他再查找方面比双链表效率低。我先不设计双链表的原因是怕自己变成完全copy开源代码了,所以单链表我最近看的开源代码中没有印象,那么用单链表全都是我自己设计出来的。另外,单纯的去用数据结构,那么就没有意思,稍微带点应用,所以我设计的了简单的借书系统,用c语言面向对象的思路去设计,主要围绕创建借书单来运用的。
关于test1.c中的单链表的创建head,插入(头插和尾插),删除,查找,遍历,还没有设计到抽象,主要是实现下,另外尝试下不同的接口传递,代码的实现方式也都是不同的。在test1.c中删除我用了3种方式去实现。REMOVE_FROM_SLIST中用了qemu之前看到的成员名称替代法,removeFromSlist的参数包括了成员,感觉复用性不好。removeFromSlist_2的参数都是node,复用性还是比较好的,这个类似于linux驱动的传参。
printf("\n7) Return book ID 2!\n");
removeFromSlist(booksystem,2);
REMOVE_FROM_SLIST(booksystem,bookID,2);
removeFromSlist_2(booksystem,customer2);
#define REMOVE_FROM_SLIST(head,elem,item) \
sl *p = head; \
sl *n = p->next; \
uint8 found = 0; \
while(n) \
{ \
if(n->elem == item) \
{ \
found = 1; \
p->next = n->next; \
free(n); \
break; \
} \
p = n; \
n = n->next; \
} \
if(found == 0) \
{ \
LOG_TRACE("Can't find Book ID!"); \
}
/**
* remove from list by node
* @param head: head of single list
* @param node: book id which to be delete from list
*/
void removeFromSlist(sl *head,uint8 bookid)
{
/* Due to remove, so we shall save two point. */
sl *p = head;
sl *n = p->next;
boolean found = 0;
while(p)
{
if(n->bookID == bookid)
{
found = 1;
printf("Book ID %d is returned for person ID 0x%X\n",n->bookID,n->personID);
p->next = n->next;
free(n);
break;
}
p=n;
n=n->next;
}
if(found == 0)
{
LOG_TRACE("Can't find Book ID!");
}
LOG_TRACE("Remove single list done!");
}
/**
* remove from list by node
* @param head: head of single list
* @param node: node of list which to be delete from list
*/
void removeFromSlist_2(sl *head,sl *node)
{
/* Due to remove, so we shall save two point. */
sl *p = head;
sl *n = p->next;
boolean found = 0;
while(n)
{
if(n == node)
{
found = 1;
printf("Book ID %d is returned for person ID 0x%X\n",n->bookID,n->personID);
p->next = n->next;
free(n);
break;
}
p=n;
n=n->next;
}
if(found == 0)
{
LOG_TRACE("Can't find this node!");
}
LOG_TRACE("Remove single list done!");
}
三,test2中精华说明
重点介绍test2.c,对象结构体封装用了小链表方式。进行了数据和链表的隔离,并且小链表放在了结构体的第一个元素。另外就是对于对象的action仿照了linux驱动的注册钩子的方式。
typedef struct _sl
{
struct _sl *next;
} sl_t;
typedef struct
{
uint8 bookID; /* book id */
uint16 personID; /* person id */
boolean bookStatus; /* 0:return 1:borrow */
} system_t;
typedef struct _booksystem_t
{
sl_t sl; /* shall be put in the top of struct */
system_t obj; /* obj */
/* actions */
void (*initbook)(struct _booksystem_t *);
void (*borrowbooks)(struct _booksystem_t *,struct _booksystem_t *);
void (*returnbooks)(struct _booksystem_t * ,uint8 );
void (*printborrowbooklist)(struct _booksystem_t *);
void (*searchbooks)(struct _booksystem_t * ,uint8 );
} booksystem_t;
然后这些钩子函数注册的函数设计思路和test1是一样的。然后用钩子的灵活性就体现了,一开始注册是头插appleBooks->borrowbooks = insertSList_head;后来注册为尾插appleBooks->borrowbooks = insertSList_tail;
test2.c
```c
/******************************************************************************
Project : Single List
Description : create;insert;remove;search;traversal functions
CPU and Compiler : win10 codeblocks mingw32
Author : AppleCai
Date : 2020-04-23
*******************************************************************************/
/*********************
* INCLUDES
*********************/
#include <stdio.h>
#include <stdlib.h>
#include "../typedef.h"
/**********************
* MACROS
**********************/
//#define MAXNUM_OF_BOOKS 5
/* the status of borrow of book */
#define BOOKED 1
#define RETURNED 0
/**********************
* TYPEDEFS
**********************/
typedef struct _sl
{
struct _sl *next;
} sl_t;
typedef struct
{
uint8 bookID;
uint16 personID;
boolean bookStatus;
} system_t;
typedef struct _booksystem_t
{
sl_t sl; /* shall be put in the top of struct */
system_t obj;
void (*initbook)(struct _booksystem_t *);
void (*borrowbooks)(struct _booksystem_t *,struct _booksystem_t *);
void (*returnbooks)(struct _booksystem_t * ,uint8 );
void (*printborrowbooklist)(struct _booksystem_t *);
void (*searchbooks)(struct _booksystem_t * ,uint8 );
} booksystem_t;
/**********************
* GLOBAL VAR
**********************/
/**********************
* CONST VAR
**********************/
/**********************
* Functions Implement
**********************/
void init_slist(booksystem_t * head)
{
/* initlize head */
booksystem_t *p = head;
p->obj.bookID = 0;
p->obj.bookStatus = RETURNED;
p->obj.personID = 0;
p->sl.next = NULL;
#if 0
/* initlize other nodes */
uint8 i = 0;
for (i = 0; i < MAXNUM_OF_BOOKS-1; i++)
{
booksystem_t *a=(booksystem_t*)malloc(sizeof(booksystem_t));
a->obj.bookID = i+2;
a->obj.bookStatus = RETURNED;
a->obj.personID = 0;
a->sl.next = NULL;
p->sl.next = a;
p=a;
}
#endif
LOG_TRACE("init the head of single list done!");
}
void print_slist(booksystem_t * head)
{
booksystem_t *p = (booksystem_t *)head->sl.next;
while(p)
{
printf("Book ID %d borrowed for person ID 0x%X\n",p->obj.bookID,p->obj.personID);
p = (booksystem_t *)p->sl.next;
}
LOG_TRACE("Traversal single list done!");
}
void insertSList_head(booksystem_t *head,booksystem_t *node)
{
node->sl.next = head->sl.next;
head->sl.next = (sl_t *)node;
LOG_TRACE("head insert for single list done!");
}
void insertSList_tail(booksystem_t *head,booksystem_t *node)
{
booksystem_t *p = head;
/* find the last node when p->next is null */
while(p->sl.next)
{
p = (booksystem_t *)p->sl.next;
}
/* change null to new node */
p->sl.next = (sl_t *)node;
node->sl.next = NULL;
LOG_TRACE("tail insert for single list done!");
}
#define REMOVE_FROM_LIST(st,head,elem,item) \
st *p = head; \
st *n = (st *)p->sl.next; \
uint8 found = 0; \
while(n) \
{ \
if(n->obj.elem == item) \
{ \
found = 1; \
p->sl.next = n->sl.next; \
free(n); \
break; \
} \
p = n; \
n = (st *)n->sl.next; \
} \
if(found == 0) \
{ \
LOG_TRACE("Can't find Book ID!"); \
}
void removefromSlist(booksystem_t * head,uint8 item)
{
REMOVE_FROM_LIST(booksystem_t,head,bookID,item);
}
void search_Slist(booksystem_t *head,uint8 bookid)
{
booksystem_t *p = (booksystem_t *)head->sl.next;
boolean found = 0;
while(p)
{
if(p->obj.bookID == bookid)
{
found = 1;
printf("Book ID %d is borrowed for person ID 0x%X\n",p->obj.bookID,p->obj.personID);
break;
}
p = (booksystem_t *)p->sl.next;
}
if(found == 0)
{
printf("Nobody borrow book ID %d\n",bookid);
}
LOG_TRACE("Search single list done!");
}
/**
* free list from head
* @param head: head of single list
*/
void DeInitAll(booksystem_t * head)
{
booksystem_t *p = head;
booksystem_t *q;
while(p)
{
q = (booksystem_t *)p->sl.next; /* save next */
free(p); /* free current point */
p = q; /* point to the next obj */
}
}
void slist2(void)
{
printf("you select Single list test example 2!\n");
/*********** Init *****************************/
booksystem_t *appleBooks; /* head */
appleBooks = (booksystem_t *)malloc(sizeof(booksystem_t));
appleBooks->initbook = init_slist;
appleBooks->borrowbooks = insertSList_head;
appleBooks->returnbooks = removefromSlist;
appleBooks->printborrowbooklist = print_slist;
appleBooks->searchbooks = search_Slist;
/* initlize head */
printf("\n1) Initlize Apple Book System!\n");
if(appleBooks->initbook)
{
appleBooks->initbook(appleBooks);
}
/* insert node */
printf("\n2) Three person borrow books!\n");
if(appleBooks->borrowbooks)
{
uint8 i = 0;
for (i = 0;i<3;i++)
{
booksystem_t *customer = (booksystem_t *)malloc(sizeof(booksystem_t));
customer->obj.bookID = i+1;
customer->obj.bookStatus = BOOKED;
customer->obj.personID = 0x1001+i;
customer->sl.next = NULL;
appleBooks->borrowbooks(appleBooks,customer);
printf("Book ID %d is borrowed for person ID 0x%X\n",customer->obj.bookID,customer->obj.personID);
}
}
/* traversal */
printf("\n3) Print borrow-list of booksystem!\n");
if(appleBooks->printborrowbooklist)
{
appleBooks->printborrowbooklist(appleBooks);
}
/* search */
printf("\n4) Check the status of book ID 2!\n");
if(appleBooks->searchbooks)
{
appleBooks->searchbooks(appleBooks,2);
}
/* remove */
printf("\n5) Return book ID 2!\n");
if(appleBooks->returnbooks)
{
appleBooks->returnbooks(appleBooks,2);
}
printf("\n6) Print borrow-list of booksystem again!\n");
if(appleBooks->printborrowbooklist)
{
appleBooks->printborrowbooklist(appleBooks);
}
/* insert tail */
appleBooks->borrowbooks = insertSList_tail;
printf("\n7) Next person borrow a book!\n");
if(appleBooks->borrowbooks)
{
booksystem_t *customer = (booksystem_t *)malloc(sizeof(booksystem_t));
customer->obj.bookID = 4;
customer->obj.bookStatus = BOOKED;
customer->obj.personID = 0x1004;
customer->sl.next = NULL;
appleBooks->borrowbooks(appleBooks,customer);
}
printf("\n8) Print borrow-list of booksystem again!\n");
if(appleBooks->printborrowbooklist)
{
appleBooks->printborrowbooklist(appleBooks);
}
DeInitAll(appleBooks);
/*********** End ******************************/
printf("\nPlease select next test item[1-9],print q to exit!Please:");
}
四,运行结果
**************************************************************
****Welcome to miniC V1.0.0 project! Copyright by AppleCai****
**************************************************************
Please select test item[1-9],print q to exit!Please:1
you select Single list test example 1!
LOG:[initSlist: init single list done! (in test1.c line #48)]
1) The first person borrows a book!
Customer ID(0x1000),borrowed a book ID(1)
LOG:[insertList_head: head insert for single list done! (in test1.c line #55)]
2) The second person borrows a book!
Customer ID(0x1001),borrowed a book ID(2)
LOG:[insertList_head: head insert for single list done! (in test1.c line #55)]
3) The third person borrows a book!
Customer ID(0x1002),borrowed a book ID(3)
LOG:[insertList_head: head insert for single list done! (in test1.c line #55)]
4) Print borrow-list of bookSystem!
Book ID 3 borrowed for person ID 0x1002
Book ID 2 borrowed for person ID 0x1001
Book ID 1 borrowed for person ID 0x1000
LOG:[printAllSlist: Traversal single list done! (in test1.c line #88)]
5) Check the status of book ID 2!
Book ID 2 is borrowed for person ID 0x1001
LOG:[searchSlist: Search single list done! (in test1.c line #108)]
6) Check the status of book ID 4!
Nobody borrow book ID 4
LOG:[searchSlist: Search single list done! (in test1.c line #108)]
7) Return book ID 2!
Book ID 2 is returned for person ID 0x1001
LOG:[removeFromSlist: Remove single list done! (in test1.c line #153)]
LOG:[slist: Can't find Book ID! (in test1.c line #223)]
LOG:[removeFromSlist_2: Can't find this node! (in test1.c line #180)]
LOG:[removeFromSlist_2: Remove single list done! (in test1.c line #182)]
8) Print borrow-list of bookSystem again!
Book ID 3 borrowed for person ID 0x1002
Book ID 1 borrowed for person ID 0x1000
LOG:[printAllSlist: Traversal single list done! (in test1.c line #88)]
9) The fourth person borrows a book ID 2!
Customer ID(0x1004),borrowed a book ID(2)
LOG:[insertList_tail: tail insert for single list done! (in test1.c line #69)]
10) Print borrow-list of booksystem again!
Book ID 3 borrowed for person ID 0x1002
Book ID 1 borrowed for person ID 0x1000
Book ID 2 borrowed for person ID 0x1004
LOG:[printAllSlist: Traversal single list done! (in test1.c line #88)]
Please select next test item[1-9],print q to exit!Please:2
you select Single list test example 2!
1) Initlize Apple Book System!
LOG:[init_slist: init the head of single list done! (in test2.c line #79)]
2) Three person borrow books!
LOG:[insertSList_head: head insert for single list done! (in test2.c line #96)]
Book ID 1 is borrowed for person ID 0x1001
LOG:[insertSList_head: head insert for single list done! (in test2.c line #96)]
Book ID 2 is borrowed for person ID 0x1002
LOG:[insertSList_head: head insert for single list done! (in test2.c line #96)]
Book ID 3 is borrowed for person ID 0x1003
3) Print borrow-list of booksystem!
Book ID 3 borrowed for person ID 0x1003
Book ID 2 borrowed for person ID 0x1002
Book ID 1 borrowed for person ID 0x1001
LOG:[print_slist: Traversal single list done! (in test2.c line #89)]
4) Check the status of book ID 2!
Book ID 2 is borrowed for person ID 0x1002
LOG:[search_Slist: Search single list done! (in test2.c line #156)]
5) Return book ID 2!
6) Print borrow-list of booksystem again!
Book ID 3 borrowed for person ID 0x1003
Book ID 1 borrowed for person ID 0x1001
LOG:[print_slist: Traversal single list done! (in test2.c line #89)]
7) Next person borrow a book!
LOG:[insertSList_tail: tail insert for single list done! (in test2.c line #110)]
8) Print borrow-list of booksystem again!
Book ID 3 borrowed for person ID 0x1003
Book ID 1 borrowed for person ID 0x1001
Book ID 4 borrowed for person ID 0x1004
LOG:[print_slist: Traversal single list done! (in test2.c line #89)]
Please select next test item[1-9],print q to exit!Please:q
Bye Apple Cai!
Please press any key to exit the miniC project!
五,总结
学以致用乐趣无穷,为了我7年后的终极目标,现在就开始准备自己的库,顺便把基础知识再回顾下。完成后还是很有成就感的呢!我喜欢别人用我做的轮子,不喜欢用别人的轮子,因为在我思维里做轮子的是创造者,用轮子的会受到限制,只是使用者。其实用轮子的也可以二次开发,或者拿着轮子继续发明创造,只是我个人比较喜欢自己做轮子罢了。