環境:STM32F103C8T6,MDK5函數
在最近的一個項目的開發中,每當調用到一個函數,程序就直接跑飛。debug跟進去看不出什麼邏輯錯誤,但發現函數內局部變量聲明以後,全局變量的值被清零,後來查看局部變量地址已經超出棧的範圍,因而肯定是棧溢出。若是不稍微瞭解一下堆棧,在開發過程當中可能碰到各類奇怪的錯誤。debug
MAP 文件是程序的全局符號、源文件和代碼行號信息的惟一的文本表示方法,它能夠在任何地方、任什麼時候候使用,不須要有額外的程序進行支持。設計
在MDK5中,在項目中雙擊Target就能自動打開.map文件。blog
Startup.s文件是系統的啓動文件,主要包括堆和棧的初始化配置、中斷向量表的配置以及將程序引導到main( )函數等。內存
Startup.s主要完成三個工做:棧和堆的初始化、定位中斷向量表、調用Reset Handler。開發
棧(stack)空間,用亍局部變量,函數調時現場保護和返回地址,函數的形參等。get
堆(heap)空間,主要用亍勱態內存分配,也就是說用 malloc,calloc, realloc 等函數分配的變量空間是在堆上。it
在map文件中搜索STACK或者HEAP,在接近文件底部的位置能夠看到SRAM的分配,以下圖。程序設計
從上圖中能夠看出SRAM空間用來存放:一、各個文件中聲明和定義的全局變量、靜態數據和常量;二、HEAP區;三、STACK區。變量
STM32的堆棧是存放在SRAM中的,分配堆棧大小須要考慮SRAM容量。
在.map文件中的Image Symbol Table底下能夠找到以下圖所示堆棧分佈信息。
堆在使用時會從低地址往上加,而棧是從__initial_sp開始往下減。以上圖中的堆棧地址爲例,malloc會從0x20002248開始往上加,局部變量的分配會從0x20004448開始往下減。若是入棧元素過大,使得入棧元素的地址訪問到了0x20002448以後的內容,就發生了棧溢出,首先會改變堆中的元素值,若是入棧元素夠大,可能會直接改變HEAP後面的全局變量。同理,當動態申請的內存過大時,堆中變量越界到棧中,此時就發送堆溢出。
避免產生這類錯誤的產生,程序設計時就應該考慮變量大小和堆棧大小是否合適。一個是減小過大的臨時變量和動態申請內存,另外一個是在SRAM空間容許的狀況下增大堆棧大小,如上圖中棧大小是8192字節,堆大小是512。
MDK5中能夠經過修改startup.s文件來設置堆棧大小,只須要修改startup.s文件中的Stack_Size和Heap_Size便可,以下圖所示。
KEIL Uvison5中默認生成的startup.s文件是隻讀的,沒法修改,只須要設置一下該文件的屬性,把只讀取消便可。