スタックメモリは関数とローカル変数にどのように使用されますか?


8

一部の値をEEPROMに保存し、いくつかの変数宣言を回避してSRAMを解放したかったのですが、EEPROMメモリはバイト単位です。

int値を格納する場合は、いくつかの式を繰り返し使用する必要があります。それらのためにいくつかの機能を作りたいと思いました。しかし、関数を作成してもSRAMメモリを占有するのではないかと心配しています。EEPROMを使用する代わりにint変数を宣言した方がよいでしょう。

関数とローカル変数はSRAMにどのように保存されますか?フラッシュメモリーからの関数ポインターのアドレスのみを保存しますか、それともすべての変数とコマンドがスタックに保存されますか?


4
EEPROMは限られた回数だけ書き込み可能で、読み取りは無制限であることを覚えておいてください。AVRのデータシートによると、EEPROMには100000サイクルしかありません。
ジッピー2014

ああ、神様!その後、EEPROMは使用できなくなりますか?データシートをチェックします!
Nafis 2014

フラッシュメモリにもライフサイクルがあります。プログラムをあまり燃やさない方が賢明です。
Nafis 2014

通常の使用では、フラッシュとEEPROMに与えられた番号はまったく問題ありません。SRAMと同じように使い始めると、方程式が変わります。
ジッピー2014

回答:


4

関数のデータのみがスタックに格納されます。そのコードはフラッシュにとどまります。ご覧のとおり、EEPROMは同じ方法でアドレス指定できないため、代わりにEEPROMを使用してSRAMの使用を減らすことはできません。EEPROMを読み取って保存するコードでも、SRAMを使用する必要があります。おそらく、保存しようとしたSRAMと同じだけのSRAMです。また、EEPROMは書き込みが遅く、寿命(各バイトへの書き込み数)が限られているため、通常スタックに置く一時データの種類を格納するために使用することは現実的ではありません。大量生産されたデバイスの一意のデバイス構成など、頻繁に変更されないデータを保存したり、まれに発生するエラーをキャプチャして後で分析したりするのに適しています。

編集:関数が呼び出されるまで その関数のスタックはありません。そのため、関数のデータのいずれかがそこに置かれるときです。関数が戻った後、スタックフレーム(SRAMの予約領域)は予約されなくなります。最終的には別の関数呼び出しで再利用されます。これは、メモリ内のCスタックのです。スタックフレームが不要になった場合は、解放されてメモリが再利用できるようになります。


このように考えているのは、関数が呼び出されたときに、その中のデータのみがスタックに格納されるということです。関数の実行後、データはスタック/ SRAMから消去されます。私は正しいですか?
Nafis 2014

5

ローカル変数と関数パラメーターはスタックに格納されます。しかし、それはそれらを使用しない理由ではありません。コンピュータはそのように機能するように設計されています。

スタックメモリは、機能がアクティブなときにのみ使用されます。関数が戻るとすぐに、メモリが解放されます。スタックメモリは良いものです。

多くのレベルの再帰で再帰関数を使用したり、スタックに大きな構造をたくさん割り当てたりしたくありません。ただし、通常の使用で問題ありません。

6502スタックはわずか256バイトですが、Apple IIは問題なく動作します。


つまり、関数は、呼び出されたときにのみ、一時的にスタックにすべてのローカル変数、パラメーター、および式と共に保存されるということですか?そうでなければ、それはプログラム/フラッシュメモリにとどまりますか?実行後、スタックから消去されますか?Arduinoフォーラムについてですが、実際にはArduinoについて話していました。
Nafis 14

いいえ、関数のパラメーターとローカル変数だけがスタックにあります。関数のコードはスタックに保存されません。これを考えすぎないでください。
Duncan C 14

5

AVR(Arduinoボードで伝統的に使用されているマイクロコントローラーファミリー)はハーバードアーキテクチャーです。つまり、実行可能なコードと変数は、2つの別々のメモリー(この場合はフラッシュとSRAM)にあります。実行可能コードがフラッシュメモリを離れることはありません。

関数を呼び出すと、通常、戻りアドレスがスタックにプッシュされます。例外は、関数呼び出しが呼び出し元の関数の最後で発生した場合です。この場合、代わりに呼び出し元の関数を呼び出した関数の戻りアドレスが使用されます-これはすでにスタック上にあります。
他のデータがスタックに置かれるかどうかは、呼び出し元の関数と呼び出された関数のレジスタプレッシャーに依存します。レジスタはCPUの作業領域であり、AVRには32個の1バイトレジスタがあります。レジスタにはCPU命令から直接アクセスできますが、SRAMのデータは最初にレジスタに格納する必要があります。引数またはローカル変数が大きすぎたり、多すぎてレジスターに収まらない場合にのみ、それらはスタックに入れられます。ただし、構造体は常にスタックに格納されます。

AVRプラットフォームのGCCコンパイラでスタックがどのように使用されるかの詳細については、https//gcc.gnu.org/wiki/avr-gcc#Frame_Layout
参照してください。 。


1

関数呼び出しが関数に入るとすぐに、実行される最初のコードは、関数の内部の一時変数に必要なスペースに等しい量だけスタックポインタをデクリメントすることです。これに関するすばらしい点は、すべての関数が再入可能かつ再帰的になることです。これは、それらの変数が呼び出し側プログラムのスタックに構築されているためです。つまり、割り込みが1つのプログラムの実行を停止し、実行を別のプログラムに転送する場合、それらが互いに干渉することなく、同じ関数を呼び出すことができます。


1

これまでのところ成功することなく、ここでの優れた答えが何を言っているかを示すために、コードの例を作成するためにかなり懸命に努力してきました。その理由は、コンパイラーが積極的に最適化するためです。これまでのところ、関数のローカル変数を使用しても、テストではスタックをまったく使用していません。理由は次のとおりです。


  • コンパイラーは関数呼び出しをインライン化する場合があるため、戻りアドレスがスタックにまったくプッシュされない場合があります。例:

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

    コンパイラはそれを次のように変えます。

    void loop () { digitalWrite (13, 5); }

    関数呼び出しなし、スタック使用なし。


  • コンパイラーはレジスターで引数を渡すことができるため、それらをスタックにプッシュする必要がなくなります。例:

    digitalWrite (13, 1);

    コンパイルして:

    158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>

    引数はレジスターに入れられるため、(digitalWriteを呼び出すための戻りアドレスは別として)スタックは使用されません。


  • ローカル変数はレジスターに入れることもでき、RAMを使用する必要がなくなります。これはRAMを節約するだけでなく、より高速です。

  • コンパイラーは、使用しない変数を最適化します。例:

    void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop

    今度はそれを持って、それは「バー」のために400のバイトを割り当てるようにしないのですか?いいえ:

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

    コンパイラは配列全体を最適化しました。これは、実際にa digitalWrite (9, 3)を実行しているだけで、それが生成するものであることがわかります。


話の教訓:コンパイラを考えようとしないでください。


ほとんどの重要な関数は、スタックを使用して一部のレジスタを保存するため、これらを使用してローカル変数を保持できます。次に、関数のスタックフレームに、その呼び出し元に属するローカル変数が含まれているという面白い状況があります。
Edgar Bonet、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.