1寫在前面
我們都知道堆棧位于RAM中,現在MCU的RAM相對較大(幾十上百K),所以分配的堆棧也是足夠大,很多人都不怎么關注這個堆棧的大小。
但是,以前MCU的RAM比較小,甚至1K都不到,所以,以前的工程師就比較關心堆棧的大小。
對于小項目而言,可能我們不用關心堆棧大小。
但是,如果項目大了,你就要注意了,你堆棧大小設置不合理,很有可能導致Fault。
想要知道堆棧有多大才合適,你就需要明白堆棧的作用,下面讓大家進一步了解堆棧。
2關于堆棧的基礎知識
我們先看一下兩點經典的知識。
1.程序的內存分配
一個由C/C 編譯的程序占用的內存分為以下幾個部分:
棧區(stack):由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。
堆區(heap):一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩回事,分配方式類似于鏈表。
全局區(靜態區)(static):全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后由系統釋放。
文字常量區:常量字符串就是放在這里的,程序結束后由系統釋放。
程序代碼區:存放函數體的二進制代碼。
2.經典例子程序
int a = 0; //全局初始化區char *p1; //全局未初始化區main(){ int b; //棧 char s[] = "abc"; //棧 char *p2; //棧 char *p3 = "123456"; //123456\0在常量區,p3在棧上。 static int c =0;//全局(靜態)初始化區 p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得來得10和20字節的區域就在堆區。 strcpy(p1, "123456"); //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。}
3結合STM32的開發講述堆棧
從上面的描述可以看得出來,在代碼中是如何占用堆和棧的。
可能很多人還是無法理解,這里再結合STM32的開發過程中與堆棧相關的內容來進行講述。
1.如何設置STM32的堆棧大小?
這個問題在文章《STM32的啟動流程到底是怎樣的?》中,講述了在MDK-ARM、IAREWARM,以及使用STM32CubeMX設置堆棧大小的方法。
2.棧(Stack)
STM32F1默認設置值0x400,也就是1K大小。
Stack_Size EQU 0x400
函數體內局部變量:
void Fun(void){ char i; int Tmp[256]; //...}
局部變量總共占用了256*4 + 1字節的棧空間。
所以,在函數內有較多局部變量時,就需要注意是否超過我們配置的堆棧大小。
函數參數:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
這里要強調一點:傳遞指針只占4字節,如果傳遞的是結構體,就會占用結構大小空間。
提示:在函數嵌套,遞歸時,系統仍會占用棧空間。
3.堆(Heap)
Heap_Size EQU 0x200
默認設置0x200(512)字節。
我們大部分人應該很少使用malloc來分配堆空間。
雖然堆上的數據只要程序員不釋放空間就可以一直訪問,但是,如果忘記了釋放堆內存,那么將會造成內存泄漏,甚至致命的潛在錯誤。
4拓展:MDK中RAM占用大小分析
經常在線調試的人,可能會分析一些底層的內容。這里結合MDK-ARM來分析一下RAM占用大小的問題。
在MDK編譯之后,會有一段RAM大小信息:
這個大小為0x668,在進行在調試時,會出現:
這個MSP就是主堆棧指針,一般我們復位之后指向的位置,復位執向的其實是棧頂:
而MSP指向地址0x20000668是0x20000000偏移0x668而得來。
具體哪些地方占用了RAM,可以參看map文件中【Image Symbol Table】處的內容:
當然,關于map文件詳細分析,可以看我系列教程《Keil系列教程12_map文件全面解析》。
關于堆棧,其實還有很多知識可以拓展,比如:堆棧入棧、出棧,向上、向下增長方式,大小端等。大家可以自己上網了解。
-
mcu
+關注
關注
146文章
17148瀏覽量
351198 -
堆棧
+關注
關注
0文章
182瀏覽量
19761
發布評論請先 登錄
相關推薦
評論