參考1:c語言中內存分配
參考2:http://blog.csdn.net/shine0181/article/details/7305551(里面有對malloc和free的源碼分析)
首先聲明,本文章所談內容是基於stm32平台。所以也許需要對stm32的存儲結構說明一下。直接上圖(圖片來自stm32官方手冊):
圖1
圖1是stm32整體存儲器結構圖,這些數字指的是存儲器的地址范圍,如下面倒數第3個Flash對應的地址是0x08000000~0x0807FFFF,
計算得出這部分空間為:512KByte,這個地址就是用來存儲代碼的;
可以看到flash所在的塊(block 0)還有其他幾個區,根據名稱可以大概知道各個區的作用,具體用途這里無需多討論。
另外block 0共有512M其中Reserved即為保留區,即可以使用外部flash擴展這個塊的存儲空間,只要地址在范圍內就可以。
下面重點來看一下block 1:
往上一個方框,block 1名稱就是SRAM,所以這個區域就是用來放置RAM。而圖中的64KBSRAM就是stm32內置的RAM,
即是說不用外部擴展RAM(MCU的基本配置就是得有CPU+RAM+外設嘛)也能運行處理器。
所以通常用一片單片機+晶振+電源就能跑了跑馬燈程序,原因就是這里,學過單片機的應該都明白這個的。
可以看到這個塊也有512MB,所以這個RAM也可以通過外部擴展至512MB,當然外部擴展的肯定不如內置的好用了。
那么這個RAM是干什么的呢?
有計算機基礎的同學應該都知道,它就是運行內存,就和電腦、手機上經常說的幾G內存是同一個東西。
它的作用就是把flash(這里只對於stm32來說)里正在運行的代碼段(函數、變量等等)放進這個內存里,
然后CPU對這里面的數據進行讀寫操作,得出你想要的結果。
而我們經常說的堆、棧也是屬於這片區域的,這里所說的堆棧和數據結構的堆棧概念不能混為一談,當然也是類似的。
stm32的堆、棧設置可以在底層驅動文件startup_stm32f10x_hd.s(這個文件要看你所用MCU的型號,這個是大容量的)里設置。
就是改下面兩句的值:
Stack_Size EQU 0x00000400 ;棧大小
Heap_Size EQU 0x00000200 ;堆大小
可以看到只能設置堆、棧的大小,不能設置起始地址和終止地址;
通過度娘查到堆、棧的起始地址設置是在.sct文件中的,具體設置方法還需進一步翻閱相關資料,這里不討論。
用默認配置,則編譯器會自動分配地址給堆棧。這個地址當然要在RAM規定的內存范圍內。
由圖1(stm32存儲器映像圖)可以知道,堆棧地址在0x20000000~0x2000FFFF范圍內,通過查找.map文件關鍵字:__initial_sp
可以找到棧地址。堆地址暫時不知道怎么搜索到,也許是因為動態分布和不連續性的原因,編譯器無法給出初始地址。
stm32的存儲器就先介紹到這里,根據圖1再往上就是外設的地址了,圖中標識的很明確,也就不細說了。
----------------------------------------------------堆、棧、malloc的使用--------------------------------------------------------------------------------
到這里stm32的堆棧的位置已經很明確了。所以可以着重來談談幾個關鍵概念的使用了。
堆的使用:
1、堆的使用是要結合malloc函數,即使用一次malloc所得到的內存空間既是屬於堆的空間。
2、堆的增長方向是向上,所以malloc申請的地址也是越來越大的,前提是連續申請且在最后一次申請后再釋放內存(free)。
則第一次申請的地址永遠小於后面申請的地址。
3、堆是不連續的,由於RAM中還存在局部變量,代碼段和棧等等,所以動態分配的內存是取暫時空閑的內存,
而不是預先划出一塊區域,這就是動態分配內存的好處。
4、使用堆的壞處,由於使用malloc申請內存時,不單只申請了所需的大小空間,還要額外暫用管理這部分空間的內存,而釋放時又只釋放申請的內存,
所以使用堆會引入內存碎片。當然如果不是在短時間內頻繁的使用malloc申請和free釋放內存,那么操作系統就有足夠的時間來回收碎片空間。
棧的使用:
1、由編譯器分配,目的是將RAM划分處一塊區域供程序運行時的局部變量參數等使用;
2、棧是一塊連續的內存空間,由上往下增長,即使用棧時地址是會越來越小的,如先聲明的局部變量比后聲明的地址要高;
3、棧是由程序(操作系統)自動分配,不會有內存碎片的問題;
4、棧的壞處:棧是固定且連續的一個大小,如果使用局部變量等超出了棧的大小則會造成內存溢出,
而編譯器通常是發現不了的,只有當程序運行到那個函數時才會發生的。這就會引入很難查找的bug。
另外如如果使用malloc申請的內存不規范使用,當釋放內存后,沒將指針地址清空,仍指向那個地址剛好是棧的地址,則會造成越界訪問。
malloc和free的使用:
1、這兩個函數是配對使用的,如果申請了一個內存,而沒有使用free釋放,反復的操作后就很快會發生內存溢出,造成難以查找的問題。
所以申請了要釋放很重要。
2、malloc使用格式:
char * Onebyte;//聲明一個char指針 Onebyte = (char*)malloc(sizeof(Onebyte));//向堆中申請一個字節內存 //下面兩句效果一樣,若在函數里 char Onearr[10];//向棧中申請10個字節 char *Onearr = (char*)malloc(10);//向堆中申請10個字節
3、free的使用:
//對於上面malloc的使用 free(Onebyte); free(Onearr);
學會內存管理可以解決一些非常棘手的問題,如:內存溢出,內存越界訪問等等。
內存溢出編譯器不能發現,引入的問題通常只能在運行代碼時才能出現,且是影響到其他代碼的運行。
在一個多任務嵌入式系統中如果出現該問題,則會變現的很奇怪,會影響到各個任務的運行。
所以才會說懂內存管理是很重要的。
