嵌入式 C语言(一)——链表管理动态结构体数组

链表管理动态结构体数组

前言

在嵌入书开发过程中会遇到一下长度不固定的结构体数组,现在整理一段链表的程序,来管理动态数据结构。直接上代码:
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};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 不使用结构、指针链表结构数组,可以使用简单的数组来存储学生信息和成绩,下面是一个示例代码: ``` #include <stdio.h> #define MAX_STUDENTS 100 int main() { int num_students, i; char names[MAX_STUDENTS][50]; int scores[MAX_STUDENTS]; printf("Enter the number of students: "); scanf("%d", &num_students); for (i = 0; i < num_students; i++) { printf("Enter the name of student %d: ", i+1); scanf("%s", names[i]); printf("Enter the score of student %d: ", i+1); scanf("%d", &scores[i]); } printf("\nStudent Scores:\n"); for (i = 0; i < num_students; i++) { printf("%s: %d\n", names[i], scores[i]); } return 0; } ``` 该示例代码中,使用二维字符数组 `names` 存储学生姓名,一维整数数组 `scores` 存储学生成绩。在输入学生信息时,依次输入学生姓名和成绩,分别存储到相应的数组中。最后输出学生信息时,遍历数组,依次输出姓名和成绩即可。 ### 回答2: 在不使用结构、指针链表结构数组的情况下,可以使用简单的数组和函数来实现C语言成绩管理系统。 一种可能的实现方是,可以定义两个一维数组,一个用于存储学生的姓名,一个用于存储学生成绩。数组的大小可以根据实际情况进行调整。 然后,可以编写一些函数来实现不同的功能,比如添加学生信息、删除学生信息、查询学生成绩等。 例如,假设最多可以存储100个学生的信息,可以定义以下数组: ``` char names[100][50]; // 用于存储学生姓名 float scores[100]; // 用于存储学生成绩 int count = 0; // 记录当前已存储的学生数量 ``` 下面是一些可能用到的功能函数的伪代码: 1. 添加学生信息: ```C void addStudent(char name[], float score) { if (count < 100) { // 将姓名和成绩分别存储到对应的数组中 strcpy(names[count], name); scores[count] = score; count++; printf("添加成功!\n"); } else { printf("无法添加,学生数量已达到上限!\n"); } } ``` 2. 删除学生信息: ```C void deleteStudent(char name[]) { int index = -1; for (int i = 0; i < count; i++) { if (strcmp(names[i], name) == 0) { index = i; break; } } if (index >= 0) { for (int i = index; i < count - 1; i++) { strcpy(names[i], names[i + 1]); scores[i] = scores[i + 1]; } count--; printf("删除成功!\n"); } else { printf("未找到该学生信息!\n"); } } ``` 3. 查询学生成绩: ```C void queryScore(char name[]) { int index = -1; for (int i = 0; i < count; i++) { if (strcmp(names[i], name) == 0) { index = i; break; } } if (index >= 0) { printf("%s的成绩是%.2f\n", names[index], scores[index]); } else { printf("未找到该学生信息!\n"); } } ``` 这只是一个简单的示例,实际上还可以根据需求添加其他功能函数。需要注意的是,由于没有使用结构和指针链表,需要手动维护数组的顺序和数量。 ### 回答3: 如果不使用结构、指针链表结构数组,我们可以考虑使用基本的变量和数组来实现C语言成绩管理系统。 首先,我们可以使用一个二维数组来保存学生的成绩信息。假设有n个学生和m门课程,那么我们可以定义一个n行m列的二维数组来存储成绩。例如,用scores[n][m]来表示。 然后,我们可以使用一个一维数组来保存每个学生的姓名。例如,用names[n]来表示。 接下来,我们可以通过循环来输入学生的成绩和姓名,将它们分别保存到相应的数组中。我们可以使用for循环来遍历每个学生和每门课程,然后使用scanf函数来输入成绩。同样地,我们可以使用for循环来输入学生的姓名。 在输入完成绩和姓名后,我们可以进行一些简单的操作,比如计算每个学生的总分和平均分,以及每门课程的平均分。 最后,我们可以使用循环来输出学生的成绩和相关统计信息,比如总分和平均分。同样地,我们可以使用循环来输出每门课程的平均分。 需要注意的是,不使用结构、指针链表结构数组可能会增加代码的复杂度和可读性。这种方法适用于简单的学生成绩管理系统,但对于更复杂的情况,使用结构、指针链表结构数组会更加方便和灵活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值