為什麼要深入理解棧?做c語言開發如果棧設定不合理或者使用不對,棧就會溢位,溢位就會遇到無法**亂飛現象。所以對棧的深入理解是非常重要的。
棧是一種受限的資料結構模型,其資料總是只能在頂部追加,利用乙個指標進行索引,頂端叫棧頂,相對的一端底部稱為棧底。棧是一種lifo**先出的資料結構。
棧就兩種操作:
再進一步**:
從c/c++程式語言的角度來看:
不同點:
棧這個資料模型的應用價值是什麼呢?先來看一下微控制器內部的可能有哪些棧應用?以stm32為例,參考iar c/c++ development
guide,p207
處理器模式
建議段名
描述supervisor
svc_stack
作業系統棧
irqirq_stack
通用(irq)中斷處理程式的堆疊。
fiqfiq_stack
用於高速(fiq)中斷處理程式的堆疊。
undefined
und_stack
堆疊用於未定義的指令中斷。 支援硬體協處理器和指令集擴充套件的軟體**。
abort
abt_stack
用於指令獲取和資料訪問儲存器中止中斷處理程式的堆疊。
如果使用rtos還有任務棧,如果是linux,其核心執行緒同樣也需要棧的支援,等等這一切的一切棧,其本質上都是利用了棧資料模型的lifo**先出的特性,乙個典型應用場景就是比如做一件事情做到一半而要轉而去做另外一件事,對於晶元程式設計而言,就需要將當前的工作做個暫存,等另外一件事情做完了,再接著回來繼續做。那麼怎麼做到呢,以乙個中斷處理為例,要記住當前的工作態有哪些資訊需要暫存呢?pc指標,區域性變數等就被壓入棧,再將中斷服務程式位址匯入pc指標,進而去執行中斷服務程式,待中斷處理完畢,在將棧裡的內容按照**先出彈出到對應的暫存器就恢復了原程式的現場,進而繼續執行。
棧在**定義大小,定多大合適?這可能很多剛接觸微控制器開發的同學不是太清楚,下面就將比較常見的iar開發環境為例如何定義棧定義棧大小的地方說明一下,這裡以iar8.4.1為例,有兩種方式可以進行棧大小設定。
其實對於比較熟悉的開發人員,上一種方式並非推薦用法。用鏈結配置檔案將具有更好的靈活性,比如可以指定乙個段的對齊方式,儲存位置,某個符號的儲存位置等等。這裡同樣為了直觀也做了乙個gif動畫,介紹如何通過鏈結檔案進行棧/堆的大小配置。
其最終的效果也一樣如預期將棧區的大小設定好了。
這裡為了比較容易的展示棧溢位的問題,在main函式利用遞迴方法計算階乘,**如下:
#include
#include
"main.h"
static uint32_t spsatte[
200]
;static uint32_t spindex =0;
/*為什麼要用浮點數,因為資料非常大整型很快就會溢位*/
float
factorial
(uint32_t n)
intmain
(void
)}
為方便觀察,將stm32f407xx_flash.icf 將棧改為256位元組
/*stm32f407xx_flash.icf 將棧改為256位元組*/
define symbol __icfedit_size_cstack__ =
0x200
;define symbol __icfedit_size_heap__ =
0x200
;
全編譯後通過map檔案來看下棧/堆的分配情況:
"p2"
, part 3 of 3
:0x400
cstack 0x2000'05d8 0x200
cstack uninit 0x2000'05d8 0x200
heap 0x2000'07d8 0x200
heap uninit 0x2000'07d8 0x200
-0x2000'09d8 0x400
直觀些,翻譯成下圖,cstack段分配在0x2000 05d8-0x2000 07d8,堆分配在0x2000 07d8-0x2000 09d8。
圖為什麼沒有將0x2000 07d8畫在棧區呢?通過除錯發現,這個字空間沒有用做棧的實際儲存。將工程設定成simulation模式,debug進入main.o勾選掉,我們來計算20的階乘,來具體看一下:
對這個**解讀一下:
__vector_table ;向量表
dcd sfe(cstack) ;這條命令會將程式的cstack起始位址裝載給sp_main
dcd reset_handler ; reset handler復位向量
棧的變化情況:
stack test:
msp->
200007a8
msp->
20000790
msp->
20000778
msp->
20000760
msp->
20000748
msp->
20000730
msp->
20000718
msp->
20000700
msp->
200006e8
msp->
200006d0
msp->
200006b8
msp->
200006a0
msp->
20000688
msp->
20000670
msp->
20000658
msp->
20000640
msp->
20000628
msp->
20000610
msp->
200005f
8msp->
200005e0
factorial(20
)=2432902023163674771.785700
/*結算結果與用計算器一致*/
每呼叫一次階乘函式,棧就壓入4個字,由上面還可以看到第20次進入時,棧指標為0x200005e0,如果再壓入4個字棧指標會變成0x200005c8,是這樣嗎,結果還對嗎?將n改為21編譯執行,來看一看:
看到了吧,驚喜來了,棧溢位了,程式已經不聽話了,完全不知道在幹嘛了。所以棧溢位的後果是極端危險的,完全無法預期,程式會帶來什麼後果。
stm32程式設計學習
今天學習了stm 32程式設計引用按鍵 按鍵 如下 ifndef key h define key h include sys.h define key0 gpio readinputdatabit gpioe,gpio pin 4 讀取按鍵0 define key1 gpio readinputd...
STM32彙編程式設計
1.實驗環境 1.野火stm32指南者 stm32f103vet6 2.keil5 2.環境搭建新建工程 新增原始檔 s 連線開發板,開始debug 生成的hex檔案 原始碼led0 equ 0x40010c00 rcc apb2enr equ 0x40021018 gpioa crh equ 0x...
STM32啟動檔案深度解析
stack size equ 0x00000400 定義棧空間大小為0x00000400,此語句等價於c define stack size 0x00000400 area stack,noinit,readwrite,align 3 定義棧,可讀寫,8位元組對齊 stack mem space s...