說明
- C/C++軟件運行時,內存根據使用方式的不同分為堆內存和棧內存,棧內存使用有以下特征:
- 棧內存使用(申請、釋放)由系統自動分配和釋放,程序員不用做任何操作。
- 棧內存重復使用,進入函數時數據入棧,函數執行完數據出棧。
- 函數中的局部變量以及實參保存在棧內存中。
內存模型
- 棧內存處于進程虛擬內存的高地址,從高往低擴展。
- 堆內存處于進程虛擬內存的低地址,從低往高擴展。
原理
- 在數據結構領域,棧是一種僅在尾部進行插入或刪除操作的線性表,以后進先出的規則管理數據,先進入的數據被壓入棧底,最后的數據在棧頂,壓入數據叫做入棧,彈出數據叫做出棧。
- 函數調用關系也是一種棧的形式,先進后出,第一個函數(main)最后執行完,末端函數最先執行完。
- 因此使用棧的形式來管理函數調用中的臨時內存使用(局部變量等)是非常合適的,而非臨時的內存使用則需要通過另外一種方式(堆內存)來管理,所以系統將進程的虛擬內存劃分為棧內存和堆內存。
- 棧內存和堆內存本質上就是一塊內存空間,只是不同的使用方式而已,對于編譯器就是不同的使用規范。
操作單元
- 入棧,出棧的操作單元并不是單個變量,而是整個函數,整個函數所需棧內存大小在編譯時就能確認,入棧和出棧都是以整個函數需要的棧內存大小為單位一次性擴大/縮小棧內存空間。
- 以及其它使用考量,例如:函數調用棧回溯等,操作單元不僅僅是函數臨時變量(局部變量,實參等)所需的棧內存空間,編譯器還會保存一些其它信息,例如:為了解決函數調用棧中間函數跳轉問題,需要將LR保存在棧中。
- 整個操作單元就叫做棧幀。
棧幀(Stack Frame)
- 每一次函數調用,都會在棧內存上維護一個獨立的棧幀(stack frame),棧幀大小是編譯時確認的,就是該函數需要占多少棧空間.每個獨立的棧幀一般包括:
- 函數參數
- 臨時變量: 包括函數的非靜態局部變量以及編譯器自動生成的其他臨時變量
- 函數調用的上下文,例如:幀指針(Frame Pointer)和返回地址(LR)等
入棧、出棧操作
- 不同平臺,函數調用的入棧和函數結束的出棧操作有細微差別,ARMV8平臺操作。