題:
堆棧存儲器如何用於函數和局部變量?
Nafis
2014-07-04 23:15:21 UTC
view on stackexchange narkive permalink

我想將一些值保存到EEPROM,還想通過避免一些變量聲明來釋放SRAM,但是EEPROM存儲器是按字節的。

如果我想存儲一個int值,我有重複使用一些表達式。我以為我會為它們做一些功能。但是我擔心,如果創建一個函數,它仍然會佔用SRAM內存,最好是聲明一個int變量而不是使用EEPROM。

函數和局部變量如何存儲在SRAM中?它是僅存儲閃存中功能指針的地址還是所有變量和命令都存儲在堆棧中?

請記住,EEPROM只能寫入有限的次數,而讀取則不受限制。根據AVR數據表,EEPROM僅具有100000個週期,這聽起來很多,但是當您嘗試將其用作SRAM時,它將僅持續很短的時間。
我的天啊!在那之後,EEPROM會失效嗎?我要檢查數據表!
閃存也具有生命週期。最好不要大量刻錄程序。
在正常使用情況下,閃存和EEPROM的編號完全沒有問題。當您開始使用方程式時,就像使用SRAM一樣。
五 答案:
Duncan C
2014-07-04 23:52:09 UTC
view on stackexchange narkive permalink

局部變量和函數參數存儲在堆棧中。但是,這不是不使用它們的原因。計算機是按這種方式工作的。

僅在功能啟用時才使用堆棧內存。函數返回後,將立即釋放內存。堆棧存儲器是一件好事。

您不想使用具有很多遞歸級別的遞歸函數,也不想在堆棧上分配很多大型結構。但是,正常使用是可以的。

6502堆棧只有256個字節,但是Apple II可以正常工作。

因此,您的意思是該函數及其所有局部變量,參數和表達式將僅在調用時臨時保存到堆棧中?否則它將保留在程序/閃存中?執行後,是否會將其從堆棧中擦除?我實際上是在談論Arduino,因為它是Arduino論壇,但我沒有提到。
不,只有函數的參數和局部變量在堆棧中。該功能的代碼未保存在堆棧中。不要想太多。
user2973
2014-07-06 12:23:26 UTC
view on stackexchange narkive permalink

AVR(傳統上在Arduino板上使用的微控制器系列)是哈佛架構,這意味著可執行代碼和變量位於兩個單獨的存儲器中,在本例中為閃存和SRAM。可執行代碼永遠不會離開閃存。

調用函數時,返回地址通常被壓入堆棧-例外是函數調用發生在調用函數的末尾。在這種情況下,將改為使用調用調用函數的函數的返回地址-它已在堆棧中。
是否將其他任何數據放在堆棧中取決於調用函數和寄存器中的寄存器壓力。稱為函數。寄存器是CPU的工作區域,AVR具有32個1字節寄存器。可以通過CPU指令直接訪問寄存器,而SRAM中的數據首先必須存儲在寄存器中。僅當參數或局部變量太大或太多而無法容納在寄存器中時,它們才會被放入堆棧中。但是,結構始終存儲在堆棧中。

您可以在以下位置閱讀有關AVR平台上GCC編譯器如何使用堆棧的詳細信息: https://gcc.gnu.org / wiki / avr-gcc#Frame_Layout
閱讀“框架佈局”和“呼叫約定”部分。

JRobert
2014-07-04 23:46:37 UTC
view on stackexchange narkive permalink

僅函數的數據存儲在堆棧中;它的代碼保留在閃存中。您不能真正通過使用EEPROM來減少SRAM的使用,因為如您所見,EEPROM不能以相同的方式尋址。讀取和存儲EEPROM的代碼還需要使用一些SRAM-可能與您要保存的SRAM一樣多! EEPROM的寫入速度也很慢,並且具有有限的生命週期(每個字節的寫入次數),這兩者都使得存儲通常放在堆棧中的臨時數據種類不切實際。它更適合保存不經常更改的數據,例如用於批量生產設備的獨特設備配置,或捕獲不頻繁的錯誤以供以後分析。

已編輯:該函數直到該函數已被調用,所以是的,那就是當該函數的任何數據放入該函數時。函數返回後發生的事情是不再保留其堆棧幀(其SRAM的保留區域)。最終它將被另一個函數調用重新使用。 這是內存中C堆棧的圖。當堆棧框架不再有用時,只需釋放它,並使其內存可重複使用即可。

我在想這樣,當調用函數時,只有其中的數據才存儲在堆棧中。執行該功能後,將從堆棧/ SRAM中擦除數據。我對嗎?
Paul Dent
2015-08-06 00:54:01 UTC
view on stackexchange narkive permalink

在函數調用進入函數後,立即執行的第一個代碼將堆棧指針遞減等於該函數內部臨時變量所需空間的數量。妙處在於,所有函數因此成為可重入和遞歸的,因為它們的變量是建立在調用程序的堆棧上的,這意味著如果中斷中止一個程序的執行並將其轉移給另一個程序,它也可以調用同一程序功能,而不會彼此干擾。

Nick Gammon
2015-08-06 02:24:01 UTC
view on stackexchange narkive permalink

我一直在努力嘗試編寫一些示例代碼,以演示此處的出色答案所講的內容,但到目前為止沒有成功。原因是編譯器積極地優化了東西。到目前為止,即使在函數中使用局部變量,我的測試也根本沒有使用堆棧。原因是:


  • 編譯器可能會內聯函數調用,因此返回地址可能根本不會被壓入堆棧。示例:

    void foo(字節a){digitalWrite(13,a); } void loop(){foo(5); }

    編譯器將其轉換為:

    無效循環(){digitalWrite(13,5); }

    不調用函數,不使用堆棧。


  • 編譯器可能在其中傳遞參數註冊,因此省去了將它們壓入堆棧的麻煩。示例:

    digitalWrite(13,1);

    編譯為:

    158:8d e0 ldi r24, 0x0D; 1315a:61 e0 ldi r22,0x01; 115c:0e 94 05 01呼叫0x20a; 0x20a <digitalWrite>

    參數被放入寄存器中,因此不使用堆棧(除了用於調用digitalWrite的返回地址)。


  • 可以將局部變量放入寄存器中,再次節省了必須使用RAM的時間。這樣不僅節省了內存,而且速度更快。

  • 編譯器會優化掉不需要的變量。示例:

    void foo(字節a){無符號長條[100]; bar [1] = a; digitalWrite(9,條[1]); } void loop(){foo(3); } //循環結束

    現在,得到了,可以為“ bar”分配400個字節,不是嗎?不:

    00000100 <_Z3fooh>:100:68 2f mov r22,r24 102:89 e0 ldi r24,0x09; 9104:0e 94 cd 00呼叫0x19a; 0x19a <digitalWrite> 108:08 95 ret 0000010a <loop>:10a:83 e0 ldi r24,0x03; 3 10c:0e 94 80 00呼叫0x100; 0x100 <_Z3fooh> 110:08 95 ret

    編譯器優化了整個數組!它可以表明我們實際上只是在執行 digitalWrite(9,3),這就是它生成的內容。


故事:不要試圖超越編譯器。

大多數非平凡函數都使用堆棧來保存一些寄存器,以便可以使用它們來保存局部變量。然後,我們遇到了一個有趣的情況,即函數的堆棧框架包含屬於_it的調用者_的局部變量。


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...