程序中經常面臨這樣的一個問題,需要創建一定數量的對象,但事前又不知道對象的具體個數,因此新手通常的做法都會為了能處理足夠的對象信息,創建一個足夠大的空間數組。這里舉個簡單的例子,某個應用要求管理學生信息,因此設置了這樣的一個結構體(類似于面向對象語言里說的成員屬性)
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();
}
上一篇:對GPIO_Init(GPIOx,&GPIO_InitStructure)的理解
下一篇:stm32創建鏈表相關問題
推薦閱讀
史海拾趣
隨著Eureka產品線的不斷拓展,公司開始積極拓展海外市場。通過與國際知名電子制造商的合作,Eureka的產品逐漸打入國際市場。同時,公司也積極參與國際展會和技術交流活動,提升了品牌知名度和影響力。這些努力不僅幫助Eureka擴大了市場份額,也促進了公司與國際同行的交流與合作。
Eureka在追求經濟效益的同時,也積極履行社會責任。公司注重環保和可持續發展,致力于減少生產過程中的能源消耗和廢棄物排放。同時,Eureka也積極參與公益活動,為社會做出積極貢獻。這些努力不僅提升了公司的社會形象,也促進了公司的可持續發展。
請注意,以上故事僅為虛構,并非Eureka公司的實際歷史。如有需要,請查閱相關公開資料或聯系Eureka公司以獲取準確信息。
隨著LED照明市場的不斷擴大,Heatron LED Integration憑借敏銳的市場洞察力,迅速調整市場戰略。公司不僅鞏固了在國內市場的領先地位,還積極開拓國際市場,與多家國際知名企業建立了長期合作關系。通過參加國際展會、設立海外分支機構等方式,公司成功將產品推向全球多個國家和地區,實現了品牌的國際化。
在環保日益受到重視的今天,BCD Semi公司積極響應國家環保政策,將環保理念融入公司的生產和經營中。公司采用環保材料和工藝,減少生產過程中的污染排放,提高資源利用效率。同時,BCD Semi還加強了對員工的環保培訓和教育,提高員工的環保意識,共同推動公司的可持續發展。
隨著全球對環境保護和可持續發展的重視,Fullywell也積極響應這一趨勢,開始實施綠色轉型戰略。公司投入大量資源研發環保型半導體材料和生產工藝,致力于降低生產過程中的能耗和排放。同時,Fullywell還加強了與產業鏈上下游企業的合作,共同推動綠色供應鏈的建設。這些舉措不僅提升了公司的社會責任感,也為公司的可持續發展奠定了堅實的基礎。通過綠色轉型,Fullywell在行業內樹立了良好的企業形象,吸引了更多關注環保和可持續發展的客戶和合作伙伴。
CQR SECURITY公司在追求商業成功的同時,也積極履行社會責任。公司定期舉辦網絡安全宣傳活動,提高公眾的網絡安全意識。同時,CQR還積極參與社會公益事業,為弱勢群體提供網絡安全支持和幫助。這種積極履行社會責任的舉措不僅提升了CQR的品牌形象,還為公司贏得了社會的廣泛贊譽。
這些故事雖然虛構,但反映了電子安全公司可能經歷的一些普遍發展路徑和挑戰。希望這些故事能夠為您提供一些啟發和參考。如果需要更多關于特定公司的信息,建議您查閱相關新聞報道、行業分析報告或公司官網等渠道。
硬件工程師,從事硬件開發的專職人員,熟悉模擬電子線路,數字電子線路,CPLD,有開發工具,特別是數字示波器,信號源等.主要負責示波器前端模擬電路的調試.PCB已經畫好。 您將獲得這次獲得的兩塊PCB(面板和數據采集板)以及主要元器件,還有CPLD下載線 ...… 查看全部問答∨ |
請高手幫我解決這個疑問: 由于我的筆記本沒有并口,然而導師給我的下載線只是并口到JTAG的,所以我就去市場買了根從USB口到并口的轉換線,然后連接起來使用,心想就可以下載了。 只是今天我 ...… 查看全部問答∨ |
在電路板絲網印刷過程中可以分為9個部分56個變量,這就要求大家有正確的操作程序或者就會出現以下現象。 網版不管怎樣拉,始終無法拉均勻,或者經常出現爆裂。 網布與網框粘不牢固,干的慢,或者撕不掉。 網版上110網布容易脫落 ...… 查看全部問答∨ |
|
在通過串口調試工具向modem com3口寫AT命令ATDT96169后connect 返回CONNECT后一段時間出現 NO CARRIER 在通過串口調試工具向modem com3口寫AT命令ATDT96169后connect 返回CONNECT,接 著下面出現~#}?}#?}!}#} }=}!}$}\'衹"}&} }*} } }\'}"}(}"}%}&7て拀#}%?}% ...… 查看全部問答∨ |
|
這個問題困擾我很久了,面對一大堆的芯片手冊,卻找不到編寫驅動的步驟. 芯片手冊里,詳細地描述了該芯片支持的特征,端口的描述,工作的信號圖,注意事項,結構方塊圖,物理參數... 卻總沒找到如何開始寫一個驅動的初始化過程,讀過程,寫過程,狀態的獲取 ...… 查看全部問答∨ |