浅谈链表对stm32程序的提升作用(C语言)

程序中经常面临这样的一个问题,需要创建一定数量的对象,但事前又不知道对象的具体个数,因此新手通常的做法都会为了能处理足够的对象信息,创建一个足够大的空间数组。这里举个简单的例子,某个应用要求管理学生信息,因此设置了这样的一个结构体(类似于面向对象语言里说的成员属性)

typedef struct 
{
	int num;
	int age;
	char *name;
}studentInfo;

在不引入链表的情况下,为了能管理尽可能多的学生信息,可能会这样创建这个结构体的实体:

studentInfo student[1024*100];
int studentCnt = 0;    //记录已经管理的学生数量

这样的处理方法,理论上能实现需求,而且在学生数量还小的时候,这个程序基本也不会出现明显的问题。但这样的做法存在很多方面的问题,当一个数组被创建时,其在内存中的大小就已经被固定了,所以,

1,当学生数量较小时,这个程序的处理方法浪费了大量的内存空间,特别是在资源紧缺的单片机或嵌入式系统中,这样的做法是不符合要求的。

2,当学生数量大于设置的数量时(即例子中的1024*100),即使系统中还剩有内存空间可以继续添加,而这个程序也无法再添加学生信息。

3,当学生数量比较大时,遇到删除信息的操作,程序的处理效率非常低,处理方法通常是,找个需要删除的信息的存放位置,然后把后面的信息逐步往前覆盖,如果删除的是最后一个,则设置为0,从而达到删除的目的。

如果引入链表,以上的三个问题都能完美解决,但需要配合内存管理才能实现,因此如果是嵌入式系统,还需要实现内存管理方面的功能,关于内存管理,将在下篇文章中提到。使用链表的方式,在原有的成员属性结构体的前提上,还要再封装多一层链表管理。以单向链表为例:

typedef struct 
{
	int num;
	int age;
	char *name;
}studentInfo;

typedef struct students
{
	struct students *nextStudent;    //用于指向下一个学生信息
	studentInfo *Info;     //具体的信息
}studentsTypedef; 

studentsTypedef *headStudents; //链表的头指针

看到这里,会发现用到的基本上是指针,有些同学可能会比较害怕用指针,其实用多了就会发现,指针也就是那么回事。好了,既然是指针,那么它是在内存空间的占用非常小,32位系统中只有4字节,而链表的精髓在于,具体的数据信息是通过内存管理申请内存存放,而链表结构则负责指向各自负责管理的数据,从而实现关联。

由于在定义的时候,只定义了一个头指针,那么它也只是个指向了studentsTypedef也就是链表结构体的指针,同样没有内存空间,在没有创建新增链表之前,它是一个野指针。

所以,在具体应用之前,需要先执行一个初始化操作,也就是申请空间给链表管理结构体,然后头指针指向这个空间。

//
//初始化
//
bool studentsListInit(void)
{
    //申请链表类型大小的空间,并让头指针指向它
	headStudents = (studentsTypedef*)malloc(sizeof(studentsTypedef));
	if(headStudents == NULL) return false;

    //同时要标记下一个信息为空
	headStudents->nextStudent = NULL;
	return true;
}

这里使用了一个malloc函数,作用是申请内存,在PC端编写测试程序时可以使用这个函数,需要包含的头文件时malloc.h。如果是在嵌入式系统中,直接使用malloc会导致一些内存碎片的问题,比较多的做法是自己实现一定的内存管理方法,包括申请内存,释放内存,高端的内存管理方法还会包含碎片整理的功能。如果是使用类似FreeRTOS这样的操作系统,RTOS中已经为我们提供了几种内存管理的方法,使用较广泛的heap4。

需要新增一个学生信息,实现方法可以这样写:

bool AddStudentToList(studentInfo *stuInfo)
{
    //在链表的最后加入
	studentsTypedef *p = headStudents;
	while(p->nextStudent!=NULL)
	{
		p = p->nextStudent;
	}
    
    //先申请链表结构体的空间,因为后续还要继续增加
	p->nextStudent =  (studentsTypedef*)malloc(sizeof(studentsTypedef));
	if(p->nextStudent == NULL) return false;
    
    //指向刚刚申请的空间,并为需要存放的学生信息申请对应的内存
	p = p->nextStudent;
	p->Info = (studentInfo*)malloc(sizeof(studentInfo));
	if(p->Info == NULL) 
    {
        free(p);//由于申请失败,先前申请的链表空间也要释放
        return false;
    }    

    //具体信息赋值
	memcpy(p->Info,stuInfo,sizeof(studentInfo));

    //标记这个链表的尾部
	p->nextStudent=NULL;

    //添加成功
	return true;
}

到这里可以发现,当需要添加一个学生的管理信息时,这里的程序就相应的申请一个学生信息所需要的空间,这样一来,可以实现内存的利用最大化,不会出现申请了内存但不使用的情况。到这里已经解决了上面说到的2个问题。

下面再来看删除操作:

bool deleteStudentInfo_AccordingNum(int num)
{
	bool res = false;
	studentsTypedef *p =  headStudents;
	while(p->nextStudent!=NULL)
	{
		studentsTypedef *temp = p;
		p = p->nextStudent;
		if(p->Info->num == num)
		{
			temp->nextStudent = p->nextStudent;
			//释放内存空间 
			free(p->Info);
			free(p);
			
			p=temp;
			res = true;
		}
	}
	return res;
}

可以看到,删除一个节点,只需要找到该节点的位置,至于怎么找这个节点,可以根据不同的方法,我这里只是随便举个例子。找到之后,只需要改变一下原来链表结构的指向,并释放被删除节点所占用的内存空间,整个删除操作就完成了,不需要像数组那样,找到之后,还要把后面的节点逐个往前面移动,效率非常低。当然,链表也并不是完全优于数组的,链表的访问只能从头节点开始,而数组的访问,可以直接使用下标,结合一定的算法,如二分排序法等,使用数组的方法的查找效率就高于链表的方法,具体如何选择,需要看实际的应用场景。

最后再贴上main函数中,测试的部分。

void printfStudentInfo(void)
{
	printf(" list 	     num		age		name\r\n");
	studentsTypedef *p = headStudents;
	int i=0;
	while(p->nextStudent!=NULL)
	{
		p = p->nextStudent;
		printf("  %d	%d		%d		%s\r\n",i,p->Info->num,p->Info->age,p->Info->name);
		i++;
	}	
	printf("----------------------------end--------------------\r\n");
} 


int main(int argc,char *argv[])
{
	printf("-------------List test---------------\r\n"); 
	if(!studentsListInit( ))
	{
		printf("Memory fail..\r\n");
	}

	studentInfo temp;
	for(int i=0;i<5;i++)
	{
		
		temp.age = 20+i;
		temp.num = i;
		temp.name = (char*)"A";
		
		if(!AddStudentToList(&temp))
		{
			printf("Add student %d info fail\r\n",i);
		}
	}
	
	printfStudentInfo();
	
	deleteStudentInfo_AccordingNum(4);
	
	printfStudentInfo();

	temp.age = 18;
	temp.num = 1001;
	temp.name = (char*)"TEST";
	if(!AddStudentToList(&temp))
	{
		printf("Add student %d info fail\r\n",1001);
	}
	
	printfStudentInfo();
}

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32中使用链表进行串口发送的方法如下: 首先,你可以创建一个循环链表来存储要发送的消息。循环链表的好处是可以动态拓展,并且可以方便地进行插入和删除操作。\[1\] 然后,你需要学习C语言链表的相关操作,以便正确地使用链表来存储和管理消息。你可以参考一些教程或者文档来学习链表的基本操作,比如插入节点、删除节点等。\[2\] 接下来,你可以使用串口发送函数将链表中的消息逐个发送出去。你可以通过遍历链表的方式,将每个节点中的消息发送出去。在发送时,你可以将消息转换为16进制,并在帧头和帧尾添加特定的标识符,以便接收端正确解析数据。\[3\] 需要注意的是,当发送的数据帧长度变化时,STM32仍然可以接收到相应长度的数据帧,而不需要修改程序。这使得整个过程非常方便和人性化。 #### 引用[.reference_title] - *1* *2* [在STM32上创建链表并实现LCD滚动显示串口消息](https://blog.csdn.net/Beking17113/article/details/84350927)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32串口通信 (采用链表接收不定长数据帧)](https://blog.csdn.net/qq_49979053/article/details/119861832)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值