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

发布者:码字狂徒最新更新时间:2020-01-07 来源: eefocus关键字:链表  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; //链表的头指针

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


由于在定义的时候,只定义了一个头指针,那么它也只是个指向了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 namern");

studentsTypedef *p = headStudents;

int i=0;

while(p->nextStudent!=NULL)

{

p = p->nextStudent;

printf("  %d %d %d %srn",i,p->Info->num,p->Info->age,p->Info->name);

i++;

}

printf("----------------------------end--------------------rn");

 

 

int main(int argc,char *argv[])

{

printf("-------------List test---------------rn"); 

if(!studentsListInit( ))

{

printf("Memory fail..rn");

}

 

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 failrn",i);

}

}

printfStudentInfo();

deleteStudentInfo_AccordingNum(4);

printfStudentInfo();

 

temp.age = 18;

temp.num = 1001;

temp.name = (char*)"TEST";

if(!AddStudentToList(&temp))

{

printf("Add student %d info failrn",1001);

}

printfStudentInfo();

}

关键字:链表  stm32  C语言 引用地址:浅谈链表对stm32等芯片程序中的提升作用(C语言)

上一篇:对GPIO_Init(GPIOx,&GPIO_InitStructure)的理解
下一篇:stm32创建链表相关问题

推荐阅读最新更新时间:2024-11-11 22:07

STM32开发笔记19: STM32CubeMX中定时器的配置方法
本文介绍在STM32CubeMX进行定时器的配置,产生固定时间中断的方法,以TIM2为例,步骤如下: 1、使能TIM2,指定时钟源。 2、查看数据手册,确定该定时器的内部数据总线,本文所引用的定时器内部数据总线为APB1。 3、在时钟配置中确认所选定时器的内部数据总线的时钟频率,我这里是32MHz。 4、在配置页中,选中相应的时钟,在Prescaler中输入预分频系数,在Counter Period中输入溢出系数。例如,本例中时钟为32MHz/32=1MHz,也就是一个周期为1us,我选择溢出系数为1000,则产生中断的时间为1ms。在Prescaler中输入总线频率的整数部分
[单片机]
<font color='red'>STM32</font>开发笔记19: STM32CubeMX中定时器的配置方法
STM32串口中断接收一个完整的数据帧
代码运行条件: (1) 大端发送; (2) 上位机发送一帧数据的时间间隔不能大于主循环周期; (3)数据帧满足下面格式: 帧头部(Head) 类型(Type) 长度(Length) 值(Value) CRC校验 2字节 1字节 1字节 X字节 2字节 0xaa 0x55 X void USART6_Init (void) { GPIO_InitTypeDef GPIO_InitStructur
[单片机]
STM32单片机学习(11) DS18B20温度传感器实验
本程序主要实现 DS18B20温度传感器数据获取,并利用串口通信把温度数据传至计算机 注:使用普中科技开发板测试时,需要拔掉Boot1插口,因为用到的是PA15管脚, 由开发板电路图可知,需要改变PA15 管脚的映射,将其设置成普通IO口 参考资料 DS18B20中文手册.pdf http://download.csdn.net/detail/leytton/7742193 STM32-外设篇 视频教程(Cortex-M3)-主讲人:刘洋 http://yun.baidu.com/pcloud/album/info?uk=2853967793&album_id=5492137931588632574 main.c
[单片机]
STM32-跑马灯实验代码分析
工程建立的主要步骤 1.新建3个文件夹:USER、SYSTEM、HARDWARE 2.新建led.c、led.h和test.c 3.在Target目录树上右击Manage Component,新建USER、SYSTEM、HARDWARE,并添加相关C文件(具体见前文 MDK使用方法与技巧 ),跟前面一样HARDWARE组添加led.c文件。 4.在Options for Target Target 1 对话框C/C++选项卡中Include Pathes里添加头文件路径。(这个不可少,否则编译出错,见前文 MDK使用方法与技巧 ,led.h文件不可漏掉) 5.使用SWD方式下载,具体见 MDK使用方法与技巧 ,注意reset
[单片机]
STM32的LCD12864液晶显示源程序
单片机源程序如下: #include config.h int main(void) { // int i=0,j=0; // int count=0; Stm32_Clock_Init(9);//系统时钟设置 delay_init(72); //延时初始化 //LED_Init(); //初始化与LED连接的硬件接口 Init_12864(); //初始化带字库12864液晶 // Display_string(0,0, 单片机综合设计 ); //显示第1行 // Display_st
[单片机]
<font color='red'>STM32</font>的LCD12864液晶显示源程序
STM32 CubeMX按键中断
一、GPIO 8种工作模式 输入模式: 1. GPIO_Mode_AIN 模拟输入 2. GPIO_Mode_IN_FLOATING 浮空输入 3. GPIO_Mode_IPD 下拉输入 4. GPIO_Mode_IPU 上拉输入 输出模式: 5. GPIO_Mode_Out_OD 开漏输出 6. GPIO_Mode_Out_PP 推挽输出 7. GPIO_Mode_AF_OD 复用开漏输出 8. GPIO_Mode_AF_PP 复用推挽输出 施密特触发器:当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;当输入在正负向阈值电压之间,输出不改变,只有当输入电压发生足够的变化
[单片机]
<font color='red'>STM32</font> CubeMX按键中断
STM32 存储器与堆栈问题
首先我们先来看下stm32的系统架构: 可以看出四个驱动单元:D-bus,S-bus,DMA1,DMA2。四个被动单元:SRAM,Flash,FSMC(这块我是以stm32f103zet6来写的,有些32芯片没有这个接口,AHB到APB以及连接的APB设备) 再来看下存储器映射 可以看出flash起始地址为0x0800 0000,SRAM起始地址为0x20000000,外设映射起始地址为0x4000000,再次看到第8块512M的存储器,0xE0000000地址为向量中断控制器的起始地址,查看《crotex-m3权威指南》可以了解到,这个地址在操作系统移植也很重要。 接下来讲BSS段,数据段,代码段,堆,栈 BSS段:(bs
[单片机]
STM32如何收发float类型数据?
在之前文章里提到了共用体用来传输浮点数的用法,但那篇笔记中没有详细介绍,这篇笔记我们一起来看一看具体实例。 实际应用中,我们可能需要两个设备通过串口传输浮点数据: 本篇笔记为了方便演示,使用串口助手模拟其中一个设备,本篇笔记内容如下: 我们创建一个用于管理float类型数据的共用体: unionfloat_data { floatf_data; uint8_tbyte ; }; 数据的流向如: 本次使用串口助手模拟发送设备,省略了第一步,主要看第②、③步。 创建两个共用体变量,用于发送与接收: unionfloat_datarx_float_data,tx_float_data; 收发相关代码: 左
[单片机]
<font color='red'>STM32</font>如何收发float类型数据?
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved