前言
在嵌入书开发过程中会遇到一下长度不固定的结构体数组,现在整理一段链表的程序,来管理动态数据结构。直接上代码:
typedef struct{
uint8_t (*init)(void **);//初始化链表参数是
void (*print)(void *);//打印链表的每项的NUmber
void (*del)(void *,uint8_t);
void (*des)(void *);
int (*len)(void *);//找指定变量的地址
void (*getaddr)(void *,void **,uint8_t);
void (*insert)(void **,void **,uint8_t);
uint8_t (*save)(void *);//插入空的链表项,有序
}DBManager;
#define LL_NAME LPhaseConfig//链表1的结构体名
#define LL_STRUCT_NAME PhaseConfigTable//链表1要管理的结构体名,结构体包含元素number
#define LL_STRUCT_SAVE_NAME "PhaseConfigTable"//链表1要管理的结构体存储的变量名
#define LL_MANAGER_NAME LPhaseConfigManager//链表1要管理的结构体存储的变量名
#define LL_MAX_NUM 64//链表1最大元素个数
extern DBManager LL_MANAGER_NAME;
static uint8_t LL_init(void **phead)//初始化数据连表
{
LL_NAME *p1 = NULL; //p1保存创建的新节点的地址
LL_NAME *p2 = NULL; //p2保存原链表最后一个节点的地址
uint8_t n = 0; //创建前链表的节点总数为0:空链表
p1 = (LL_NAME *)rt_malloc (sizeof(LL_NAME)); //开辟一个新节点
rt_kprintf ("malloc ADDR:0x%x\n",p1);
p2 = p1; //如果节点开辟成功,则p2先把它的指针保存下来以备后用
*phead = RT_NULL;
if(p1==NULL) //节点开辟不成功
{
printf ("\nCann't create LDetector, try it again in a moment!\n");
return 0;
}
//节点开辟成功
//开始录入数据
for(uint8_t i = 0;i<LL_MAX_NUM;i++) //连续的存储
{
rt_sprintf(tablename,"%s_%d",LL_STRUCT_SAVE_NAME,i+1);
if(ef_get_env_blob(tablename,&p1->data,sizeof(LL_STRUCT_NAME),NULL)!=0)
{
printf ("\nread %s scuss\n",tablename);
printf ("num:%d\n",p1->data.number);
printf ("num:%x\n",p1->data.lamp[0]);
n += 1; //节点总数增加1个
if(n == 1) //如果节点总数是1,则head指向刚创建的节点p1
{
*phead = p1;
p2->next = NULL; //此时的p2就是p1,也就是p1->next指向NULL。
}
else
{
p2->next = p1; //指向上次下面刚刚开辟的新节点
}
p2 = p1; //把p1的地址给p2保留,然后p1产生新的节点
p1 = (LL_NAME *) malloc (sizeof(LL_NAME));
}
else
{
break;
}
}
p2->next = NULL; //此句就是根据单向链表的最后一个节点要指向NULL
free(p1); //p1->num为0的时候跳出了while循环,并且释放p1
p1 = NULL; //特别不要忘记把释放的变量清空置为NULL,否则就变成"野指针",即 地址不确定的指针
printf ("ADDR:0x%x\n",*phead);
return n;
}
static void LL_print(void *phead) // 输出节点Number
{
LL_NAME *p;
p = phead;
if(phead != NULL) //只要不是空链表,就输出链表中所有节点
{
do
{
/*
输出相应的值:当前节点地址、各字段值、当前节点的下一节点地址。
这样输出便于读者形象看到一个单向链表在计算机中的存储结构,和我们
设计的图示是一模一样的。
*/
rt_kprintf ("Detector NUM:%d\n", p->data.number);
p = p->next; //移到下一个节点
}
while (p != NULL);
}
}
static void LL_del (void *phead,uint8_t num) //删除指定编号的节点
{
LL_NAME *p1; //p1保存当前需要检查的节点的地址
LL_NAME *p2; //p2保存当前检查过的节点的地址
if (phead == NULL) //是空链表(结合图3理解)
{
printf ("\nList is null!\n");
return;
}
//定位要删除的节点
p1 = phead;
while (p1->data.number != num && p1->next != NULL) //p1指向的节点不是所要查找的,并且它不是最后一个节点,就继续往下找
{
p2 = p1; //保存当前节点的地址
p1 = p1->next; //后移一个节点
}
if(p1->data.number == num) //找到了。(结合图4、5理解)
{
if (p1 == phead) //如果要删除的节点是第一个节点
{
phead = p1->next; //头指针指向第一个节点的后一个节点,也就是第二个节点。这样第一个节点就不在链表中,即删除
}
else//如果是其它节点,则让原来指向当前节点的指针,指向它的下一个节点,完成删除
{
p2->next = p1->next;
}
free(p1); //释放当前节点
p1 = NULL;
printf("\ndelete Detector NUM: %ld success!\n",num);
}
else //没有找到
{
printf("\nDetector NUM:%d not been found!\n", num);
}
return;
}
static void LL_des(void *head) //销毁链表
{
LL_NAME *p;
LL_NAME *pHead;
pHead = head;
if(head==NULL)
return;
while(head)
{
p=pHead->next;
free(head);
head=p;
}
return;
}
static int LL_len(void *pHead)/*返回单链表的长度 */
{
int size = 0;
LL_NAME *p;
p = pHead;
while(pHead != NULL)
{
size++; //遍历链表size大小比链表的实际长度小1
pHead = p->next;
}
return size; //链表的实际长度
}
static void LL_getAddr(void *pHead, void **node,uint8_t num)//获得指定编号的元素的地址
{
LL_NAME *p;
p = pHead;
*node = RT_NULL;
if(NULL == p)
{
printf("LL1 getElemAddr List NULL!!\n");
return;
}
if(num < 0)
{
printf("LL1 getElemAddr num<0!!!\n");
return;
}
while((p->data.number != num) && (NULL != p->next)) //判断是否到链表末尾,以及是否存在所要找的元素
{
p = p->next;
}
if((p->data.number != num) && (p != NULL))
{
printf("LL1 getElemAddr num %d nofind!!\n", num);
return;
}
if(p->data.number == num)
{
*node = p; //返回元素的地址
printf("LL getElemAddr num: %d addr: 0x%x\n", num, &(p->data.number));
}
}
static void LL1_insert(void **head,void **node,uint8_t num) //插入有序链表的某个节点的后面(从小到大)//
{
LL_NAME *p; //p保存当前需要检查的节点的地址
LL_NAME *t; //临时指针变量
LL_NAME *p1 = NULL; //p1保存创建的新节点的地址
p1 = (LL_NAME *)rt_malloc (sizeof(LL_NAME)); //开辟一个新节点
p1->data.number = num;
*node= p1;
if(*head == NULL) //处理空的有序链表
{
*head = p1;
p1->next = NULL; //插入完毕,节点总数加
return;
}
p = *head; //有序链表不为空
while(p->data.number < p1->data.number && p != NULL) //p指向的节点的学号比插入节点的学号小,并且它不等于NULL
{
t = p; //保存当前节点的前驱,以便后面判断后处理
p = p->next; //后移一个节点
}
if (p == *head) //刚好插入第一个节点之前
{
p1->next = p;
*head = p1;
}
else //插入其它节点之后
{
t->next = p1; //把node节点加进去
p1->next = p;
}
return;
}
static uint8_t LL_save(void *phead) //保存链表数据
{
LL_NAME *p1 = NULL;//p1保存创建的新节点的地址
LL_NAME *p2 = NULL;
p2 = (LL_NAME *) rt_malloc (sizeof(LL_NAME));//p2用来存放Flash读到的信息
uint8_t n;
//开始录入数据
uint8_t j;
j = LL_len(phead)+1;
for(uint8_t i = 1;i<j;i++) //只要学号不为0,就继续录入下一个节点
{
LL_getAddr(phead,(void **)&p1,i);//
if(p1 != RT_NULL)
{
n++;
sprintf(tablename,"%s_%d",LL_STRUCT_SAVE_NAME,i);
if(ef_get_env_blob(tablename,&p2->data,sizeof(LL_STRUCT_NAME),NULL)!=0)
{
if(rt_strcmp((char *)&(p2->data),(char *)&(p1->data)) != 0)//查看是否修改,若修改则写入
{
ef_set_env_blob(tablename,&p1->data,sizeof(LL_STRUCT_NAME));
rt_kprintf ("\nsave %s scuss\n",tablename);
}
}
else {
ef_set_env_blob(tablename,&p1->data,sizeof(LL_STRUCT_NAME));
rt_kprintf ("\nsave %s scuss\n",tablename);
}
}
}
rt_free(p2);
return n;
}
DBManager LL_MANAGER_NAME = {LL_init,LL_print,LL_del,LL_des,LL_len,LL_getAddr,LL_insert,LL_save};