RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度解析鏈表在STM32中的應(yīng)用

GReq_mcu168 ? 來源:開源博客 ? 作者:Firefly_cjd ? 2021-05-02 10:33 ? 次閱讀

1、為何引入鏈表

在程序中經(jīng)常面臨一個(gè)問題,我們需要保存一定數(shù)量的對(duì)象,但是對(duì)象數(shù)目是不確定的,或者說是隨時(shí)增加或減少的。這時(shí)候最簡(jiǎn)單的方法是創(chuàng)建一個(gè)足夠大的數(shù)組,用來存儲(chǔ)這些對(duì)象。我最近開發(fā)一個(gè)項(xiàng)目就遇到類似的問題,下面我把問題簡(jiǎn)化一下。

需求:通過PC下發(fā)一些矩形的坐標(biāo)和寬高信息,每個(gè)區(qū)域有個(gè)ID編號(hào),并在這些矩形內(nèi)填充一定的數(shù)據(jù)。

通常情況下,最簡(jiǎn)單易懂的做法是,限制最多5個(gè)區(qū)域,每個(gè)區(qū)域存儲(chǔ)1K數(shù)據(jù)。因此設(shè)置了這樣的一個(gè)結(jié)構(gòu)體(類似于面向?qū)ο笳Z言里說的成員屬性)。

typedef struct Area_Inf{ uint8_t ID; uint8_t X; uint8_t Y; uint8_t Width; uint8_t Height; uint8_t data_len;}Area_Inf_Typedef;

然后定義結(jié)構(gòu)體的實(shí)體。

#define Area_Num 5#define Area_cache 1024

Area_Inf_Typedef Area_Info[Area_Num];uint8_t Area_Data[Area_Num*Area_cache];//存儲(chǔ)區(qū)域的數(shù)據(jù)

/*找到ID為5的區(qū)域,并將數(shù)據(jù)拷貝出去*/void main(){ uint8_t i; uint8_t data[1024]; for(i = 0;i 《 Area_Num;i++) { if(Area_Info[i].ID == 5) { memcpy(data,&Area_Data[i*Area_cache ],Area_Info[i].data_len); } }}

上面這種做法是最簡(jiǎn)單易懂的,但不靈活,比如有客戶要求10個(gè)區(qū)域,但是每個(gè)區(qū)域存儲(chǔ)的數(shù)據(jù)很少,根本用不到1K。雖然上面的程序已經(jīng)使用了宏定義,只需要修改宏定義就能實(shí)現(xiàn)要求。但這意味著不同的客戶,需要編譯不同的固件。

#define Area_Num 10#define Area_cache 512

這樣的程序存在的問題:

1、在內(nèi)存資源很緊缺的單片機(jī)程序中,當(dāng)區(qū)域數(shù)據(jù)很少時(shí),這種程序的處理方法浪費(fèi)了大量的內(nèi)存空間。

2、數(shù)值固定,需要存儲(chǔ)更多區(qū)域,即使還有內(nèi)存,還是需要修改宏定義,重新編譯固件,不靈活。

這時(shí)需要引入鏈表來解決這個(gè)問題。

2、鏈表實(shí)現(xiàn)

鏈表實(shí)際上是線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),與數(shù)組不同的是,它是用一組任意的存儲(chǔ)單元來存儲(chǔ)線性表中的數(shù)據(jù),存儲(chǔ)單元不一定是連續(xù)的,且鏈表的長(zhǎng)度不是固定的,鏈表數(shù)據(jù)的這一特點(diǎn)使其可以非常的方便地實(shí)現(xiàn)節(jié)點(diǎn)的插入和刪除操作。鏈表的每個(gè)元素稱為一個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都可以存儲(chǔ)在內(nèi)存中的不同的位置,為了表示每個(gè)元素與后繼元素的邏輯關(guān)系,以便構(gòu)成“一個(gè)節(jié)點(diǎn)鏈著一個(gè)節(jié)點(diǎn)”的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),除了存儲(chǔ)元素本身的信息外,還要存儲(chǔ)其直接后繼信息,因此,每個(gè)節(jié)點(diǎn)都包含兩個(gè)部分,第一部分稱為鏈表的數(shù)據(jù)區(qū)域,用于存儲(chǔ)元素本身的數(shù)據(jù)信息。

6ee7ead4-9e2c-11eb-8b86-12bb97331649.png

對(duì)于上面的問題,我們使用鏈表解決,需要配合內(nèi)存管理才能實(shí)現(xiàn)。內(nèi)存管理這一塊,大家可以自己編寫內(nèi)存管理驅(qū)動(dòng),也可以使用C庫的malloc和free函數(shù)。如何字節(jié)編寫內(nèi)存管理驅(qū)動(dòng)不是本文的重點(diǎn),下文將使用C庫的malloc和free函數(shù)進(jìn)行內(nèi)存管理。

使用鏈表的方式,在原有的成員屬性結(jié)構(gòu)體的前提上,還要再封裝多一層鏈表管理。以單向鏈表為例:

typedef struct Area_Inf{ uint8_t ID; uint8_t X; uint8_t Y; uint8_t Width; uint8_t Height; uint8_t data_len; uint8_t* Area_Data;}Area_Inf_Typedef;

typedef struct Area_List_Inf{ Area_Inf_Typedef *Area_Inf; struct Area_List_Inf *next_Area_Inf; //用于指向下一個(gè)}Area_List_Inf_Typedef;

Area_List_Inf_Typedef *Head_Area_List; //鏈表的頭指針

由于在定義的時(shí)候,只定義了一個(gè)頭指針,那么它也只是個(gè)指向了Area_List_Inf_Typedef也就是鏈表結(jié)構(gòu)體的指針,同樣沒有內(nèi)存空間,在沒有創(chuàng)建新增鏈表之前,它是一個(gè)野指針。

所以,在具體應(yīng)用之前,需要先執(zhí)行一個(gè)初始化操作,也就是申請(qǐng)空間給鏈表管理結(jié)構(gòu)體,然后頭指針指向這個(gè)空間。

/*** @brief 動(dòng)態(tài)區(qū)鏈表初始化* @return int */int Area_List_Init(void){ //申請(qǐng)鏈表類型大小的空間,并讓頭指針指向它 Head_Area_List = (Area_List_Inf_Typedef*)malloc(sizeof(Area_List_Inf_Typedef)); if(Head_Area_List == NULL) return false; //同時(shí)要標(biāo)記下一個(gè)信息為空 Head_Area_List-》next_Area_Inf = NULL; return true;}

通過PC下發(fā)一個(gè)新的區(qū)域信息后,增加新區(qū)域到鏈表末尾。

/*** @brief 在鏈表末尾增加一個(gè)區(qū)域參數(shù)* @param Area_Inf 增加的區(qū)域區(qū)參數(shù)指針* @return int */int Add_Area_ToList(Area_Inf_Typedef *Area_Inf){ Area_List_Inf_Typedef *p = Head_Area_List; while(p-》next_Area_Inf!=NULL) { p = p-》next_Area_Inf; } //先申請(qǐng)鏈表結(jié)構(gòu)體的空間,因?yàn)楹罄m(xù)還要繼續(xù)增加 p-》next_Area_Inf = (Area_List_Inf_Typedef*)malloc(sizeof(Area_List_Inf_Typedef)); if(p-》next_Area_Inf == NULL) return false;//申請(qǐng)不到內(nèi)存,返回失敗 //指向剛剛申請(qǐng)的空間,并為需要存放的動(dòng)態(tài)區(qū)信息申請(qǐng)對(duì)應(yīng)的內(nèi)存 p = p-》next_Area_Inf; p-》Area_Inf = (Area_Inf_Typedef*)malloc(sizeof(Area_Inf_Typedef)); if(p-》Area_Inf == NULL) { free(p);//由于申請(qǐng)失敗,先前申請(qǐng)的鏈表空間也要釋放 return false; } memcpy(p-》Area_Inf,Area_Inf,sizeof(Area_Inf_Typedef)); /*拷貝數(shù)據(jù)*/ p-》Area_Inf-》Area_Data = (uint8_t*)malloc(Area_Inf-》data_len); if(p-》Area_Inf-》Area_Data == NULL) { free(p-》Area_Inf); free(p); return false; } memcpy(p-》Area_Inf-》Area_Data,Area_Inf-》Area_Data,Area_Inf-》data_len); //標(biāo)記這個(gè)鏈表的尾部 p-》next_Area_Inf=NULL; //添加成功 return true;}

通過PC下發(fā)一個(gè)刪除指定ID的區(qū)域命令。

/*** @brief 根據(jù)區(qū)域ID刪除動(dòng)態(tài)區(qū)* @param num 區(qū)域ID* @return int */int Delete_Area_Accordingn_ID(int num){ int res = false; Area_List_Inf_Typedef *p = Head_Area_List; while(p-》next_Area_Inf!=NULL) { Area_List_Inf_Typedef *temp = p; p = p-》next_Area_Inf; if(p-》Area_Inf-》ID == num)//匹配到對(duì)應(yīng)的值 { temp-》next_Area_Inf = p-》next_Area_Inf; //釋放內(nèi)存空間 free(p-》Area_Inf-》Area_Data); free(p-》Area_Inf); free(p); p=temp; res = true; } } return res;}

看了上面的驅(qū)動(dòng)函數(shù),相信大家已經(jīng)明白,大家可以自行編寫一些驅(qū)動(dòng),下面我實(shí)現(xiàn)的三個(gè)簡(jiǎn)單函數(shù)。

/*** @brief 根據(jù)區(qū)域ID找到鏈表* @param data_p 鏈表指針* @param num 區(qū)域ID編號(hào)* @return int */int Find_Area_According_ID(Area_Inf_Typedef **data_p,int num){ Area_List_Inf_Typedef *p = Head_Area_List; while(p-》next_Area_Inf!=NULL) { p = p-》next_Area_Inf; if(p-》Area_Inf-》ID == num)//匹配到對(duì)應(yīng)的值 { *data_p = p-》Area_Inf; return true; } } return false;}/*** @brief 刪除所有區(qū)域* */int Delete_All_Area(void){ int res = false; Area_List_Inf_Typedef *p = Head_Area_List; while(p-》next_Area_Inf!=NULL) { Area_List_Inf_Typedef *temp = p; p = p-》next_Area_Inf; temp-》next_Area_Inf = p-》next_Area_Inf; //釋放內(nèi)存空間 free(p-》Area_Inf-》Area_Data); free(p-》Area_Inf); free(p); p=temp; res = true; } return res;}/*** @brief 打印鏈表信息* */void Printf_Area_Inf(void){ int i=0; Area_List_Inf_Typedef *p = Head_Area_List; printf(“l(fā)ist ID X Y Width Height Area_Data

”); while(p-》next_Area_Inf!=NULL) { p = p-》next_Area_Inf; printf(“ %d %d %d %d %d %d %s

”,i,p-》Area_Inf-》ID,p-》Area_Inf-》X,p-》Area_Inf-》Y,p-》Area_Inf-》Width,p-》Area_Inf-》Height,p-》Area_Inf-》Area_Data); i++; } printf(“----------------------end-----------------------

”);}

3、測(cè)試函數(shù)

下面編寫一個(gè)測(cè)試函數(shù),可以測(cè)試,鏈表的初始化,增加一個(gè)區(qū)域,刪除指定區(qū)域,根據(jù)ID返回區(qū)域信息,刪除所有區(qū)域接口。

/*** @brief 鏈表測(cè)試函數(shù)* */void list_main(){ int i,j; Area_Inf_Typedef temp; Area_Inf_Typedef **data_p; data_p = NULL; printf(“------------------List test---------------------

”); if(!Area_List_Init( )) { printf(“Memory fail.。

”); } for(i=0;i《5;i++) { temp.ID = i; temp.X = 5+i; temp.Y = i; temp.Width = 10+i; temp.Height = 10+i; temp.data_len = i+1; temp.Area_Data = (uint8_t*)malloc(temp.data_len+1); for(j=0;j《temp.data_len;j++) { temp.Area_Data[j] = j+0x30; } temp.Area_Data[j] = 0; if(!Add_Area_ToList(&temp)) { printf(“Add Area %d Area_Info fail

”,i); } } Printf_Area_Inf(); printf(“

-------------Delete ID of Area is 3-------------

”); Delete_Area_Accordingn_ID(3); Printf_Area_Inf(); temp.ID = 9; temp.data_len = 10; temp.Area_Data = (uint8_t*)malloc(temp.data_len+1); for(j=0;j《temp.data_len;j++) { temp.Area_Data[j] = j+0x30; } temp.Area_Data[j] = 0; if(!Add_Area_ToList(&temp)) { printf(“Add Area %d info fail

”,temp.ID); } printf(“

--------------Add ID of Area is 9---------------

”); Printf_Area_Inf(); Find_Area_According_ID(data_p,2); temp.ID = (*data_p)-》ID; Delete_All_Area(); printf(“

--------------Delete All Area-------------------

”); Printf_Area_Inf(); while(1);}

測(cè)試結(jié)果

6ef62022-9e2c-11eb-8b86-12bb97331649.png

如果大家手中有板子可以調(diào)試,可以看《一文了解串口打印》文章,使用串口打印。如果臨時(shí)沒有板子可以debug,可以模擬測(cè)試,IAR設(shè)置如下:

選擇Simulator調(diào)試

6eff06a6-9e2c-11eb-8b86-12bb97331649.png

打開View-》TerminalI/O,就可以看到打印信息

6f0adc92-9e2c-11eb-8b86-12bb97331649.png

編輯:lyn

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • STM32
    +關(guān)注

    關(guān)注

    2270

    文章

    10895

    瀏覽量

    355729
  • 鏈表
    +關(guān)注

    關(guān)注

    0

    文章

    80

    瀏覽量

    10558

原文標(biāo)題:鏈表在STM32中的應(yīng)用

文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    深度解析研華全棧式AI產(chǎn)品布局

    人工智能邁向邊緣智能化的浪潮,研華科技通過“Edge AI+生態(tài)協(xié)同”戰(zhàn)略推動(dòng)AIoT 2.0時(shí)代的產(chǎn)業(yè)落地。本文專訪研華科技產(chǎn)品總監(jiān)邱柏儒,深度解析研華全棧式AI產(chǎn)品布局、差異化
    的頭像 發(fā)表于 12-05 09:51 ?217次閱讀

    GPU深度學(xué)習(xí)的應(yīng)用 GPUs圖形設(shè)計(jì)的作用

    隨著人工智能技術(shù)的飛速發(fā)展,深度學(xué)習(xí)作為其核心部分,已經(jīng)成為推動(dòng)技術(shù)進(jìn)步的重要力量。GPU(圖形處理單元)深度學(xué)習(xí)扮演著至關(guān)重要的角色,其強(qiáng)大的并行處理能力使得訓(xùn)練復(fù)雜的神經(jīng)網(wǎng)絡(luò)模
    的頭像 發(fā)表于 11-19 10:55 ?429次閱讀

    NPU深度學(xué)習(xí)的應(yīng)用

    設(shè)計(jì)的硬件加速器,它在深度學(xué)習(xí)的應(yīng)用日益廣泛。 1. NPU的基本概念 NPU是一種專門針對(duì)深度學(xué)習(xí)算法優(yōu)化的處理器,它與傳統(tǒng)的CPU和GPU有所不同。NPU通常具有高度并行的處理能力,能夠高效地執(zhí)行
    的頭像 發(fā)表于 11-14 15:17 ?503次閱讀

    FPGA深度神經(jīng)網(wǎng)絡(luò)的應(yīng)用

    、低功耗等特點(diǎn),逐漸成為深度神經(jīng)網(wǎng)絡(luò)邊緣計(jì)算和設(shè)備端推理的重要硬件平臺(tái)。本文將詳細(xì)探討FPGA深度神經(jīng)網(wǎng)絡(luò)的應(yīng)用,包括其優(yōu)勢(shì)、設(shè)計(jì)流程
    的頭像 發(fā)表于 07-24 10:42 ?656次閱讀

    溫度補(bǔ)償振蕩器TG-3541CE的深度解析

    溫度補(bǔ)償振蕩器TG-3541CE的深度解析
    的頭像 發(fā)表于 07-18 17:48 ?320次閱讀

    深度神經(jīng)網(wǎng)絡(luò)雷達(dá)系統(tǒng)的應(yīng)用

    深度神經(jīng)網(wǎng)絡(luò)(Deep Neural Networks,DNN)雷達(dá)系統(tǒng)的應(yīng)用近年來取得了顯著進(jìn)展,為雷達(dá)信號(hào)處理、目標(biāo)檢測(cè)、跟蹤以及識(shí)別等領(lǐng)域帶來了革命性的變化。以下將詳細(xì)探討深度
    的頭像 發(fā)表于 07-15 11:09 ?714次閱讀

    深度學(xué)習(xí)視覺檢測(cè)的應(yīng)用

    深度學(xué)習(xí)是機(jī)器學(xué)習(xí)領(lǐng)域中的一個(gè)重要分支,其核心在于通過構(gòu)建具有多層次的神經(jīng)網(wǎng)絡(luò)模型,使計(jì)算機(jī)能夠從大量數(shù)據(jù)自動(dòng)學(xué)習(xí)并提取特征,進(jìn)而實(shí)現(xiàn)對(duì)復(fù)雜任務(wù)的處理和理解。這種學(xué)習(xí)方式不僅提高了機(jī)器對(duì)數(shù)據(jù)的解釋
    的頭像 發(fā)表于 07-08 10:27 ?700次閱讀

    深度學(xué)習(xí)自動(dòng)駕駛的關(guān)鍵技術(shù)

    隨著人工智能技術(shù)的飛速發(fā)展,自動(dòng)駕駛技術(shù)作為其中的重要分支,正逐漸走向成熟。自動(dòng)駕駛系統(tǒng)深度學(xué)習(xí)技術(shù)發(fā)揮著至關(guān)重要的作用。它通過模擬人腦的學(xué)習(xí)過程,實(shí)現(xiàn)對(duì)車輛周圍環(huán)境的感知、理解和決策。本文將深入探討
    的頭像 發(fā)表于 07-01 11:40 ?754次閱讀

    5G工業(yè)路由器智慧交通車路協(xié)同應(yīng)用的深度解析

    隨著科技的飛速發(fā)展,智慧交通已成為現(xiàn)代城市發(fā)展的重要方向。智慧交通的眾多技術(shù),5G工業(yè)路由器憑借其高速、穩(wěn)定、安全等特性,成為車路協(xié)同應(yīng)用不可或缺的一環(huán)。本文將在本文中深度
    的頭像 發(fā)表于 06-18 17:32 ?424次閱讀
    5G工業(yè)路由器<b class='flag-5'>在</b>智慧交通車路協(xié)同應(yīng)用的<b class='flag-5'>深度</b><b class='flag-5'>解析</b>

    為什么STM32F0 modbus解析接收不到任何數(shù)據(jù)?

    我看了很久也找不到為什么我的modbus解析接收不到任何數(shù)據(jù),求大神幫助下,感激不盡
    發(fā)表于 04-25 06:28

    深度解析深度學(xué)習(xí)下的語義SLAM

    隨著深度學(xué)習(xí)技術(shù)的興起,計(jì)算機(jī)視覺的許多傳統(tǒng)領(lǐng)域都取得了突破性進(jìn)展,例如目標(biāo)的檢測(cè)、識(shí)別和分類等領(lǐng)域。近年來,研究人員開始視覺SLAM算法引入深度學(xué)習(xí)技術(shù),使得
    發(fā)表于 04-23 17:18 ?1284次閱讀
    <b class='flag-5'>深度</b><b class='flag-5'>解析</b><b class='flag-5'>深度</b>學(xué)習(xí)下的語義SLAM

    FPGA深度學(xué)習(xí)應(yīng)用或?qū)⑷〈鶪PU

    硬件公司供貨的不斷增加,GPU 深度學(xué)習(xí)的市場(chǎng)需求還催生了大量公共云服務(wù),這些服務(wù)為深度學(xué)習(xí)項(xiàng)目提供強(qiáng)大的 GPU 虛擬機(jī)。 但是顯卡也受硬件和環(huán)境的限制。Larzul 解釋說:
    發(fā)表于 03-21 15:19

    數(shù)組和鏈表在內(nèi)存的區(qū)別 數(shù)組和鏈表的優(yōu)缺點(diǎn)

    內(nèi)存的存儲(chǔ)方式: 數(shù)組是一種連續(xù)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu),它將元素存儲(chǔ)相鄰的內(nèi)存位置。這使得數(shù)組的訪問效率高,可以通過下標(biāo)來直接訪問任何一個(gè)元素。 鏈表是一種離散存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu),它將元素
    的頭像 發(fā)表于 02-21 11:30 ?1021次閱讀

    數(shù)組和鏈表有何區(qū)別

    數(shù)組和鏈表的區(qū)別,這個(gè)問題,不僅面試中經(jīng)常遇到,考研的同學(xué)也得掌握才行。
    的頭像 發(fā)表于 02-19 15:33 ?502次閱讀
    數(shù)組和<b class='flag-5'>鏈表</b>有何區(qū)別

    XMLHarmonyOS的生成,解析與轉(zhuǎn)換(下)

    一、XML 解析 對(duì)于以 XML 作為載體傳遞的數(shù)據(jù),實(shí)際使用需要對(duì)相關(guān)的節(jié)點(diǎn)進(jìn)行解析,一般包括解析 XML 標(biāo)簽和標(biāo)簽值、解析 XML
    的頭像 發(fā)表于 02-18 10:07 ?733次閱讀
    RM新时代网站-首页