プログラミング言語の設計でcall stackのアイデアが得られたようです。しかし、私はスタックフレームが何であるかについての適切な説明を見つけることができません(おそらく、十分な検索をしていません)。
だから私は誰かにそれをいくつかの言葉で私に説明してもらいたいと思います。
スタックフレームは、スタックにプッシュされるデータのフレームです。呼び出しスタックの場合、スタックフレームは関数呼び出しとその引数データを表します。
記憶が正しければ、関数の戻りアドレスが最初にスタックにプッシュされ、次にローカル変数の引数とスペースがプッシュされます。一緒に「フレーム」を作成しますが、これはおそらくアーキテクチャに依存します。プロセッサは各フレームにいくつのバイトがあるかを知っており、フレームがスタックからプッシュおよびポップされると、それに応じてスタックポインターを移動します。
高レベルのコールスタックとプロセッサのコールスタックには大きな違いがあります。
プロセッサの呼び出しスタックについて話すときは、アセンブリコードまたはマシンコードでバイト/ワードレベルでアドレスと値を操作することについて話します。高レベル言語について話すときは「コールスタック」がありますが、これらはランタイム環境によって管理されるデバッグ/ランタイムツールであり、プログラムの問題を(高レベルで)ログに記録できます。このレベルでは、行番号、メソッド名、クラス名などがよく知られています。プロセッサがコードを取得するまで、これらの概念はまったくありません。
スタックを非常によく理解していると、プログラムでメモリがどのように機能するかを理解でき、プログラムでメモリがどのように機能するかを理解できれば、プログラムでの関数ストアの方法を理解でき、プログラムでの関数ストアの方法を理解できれば、再帰関数の機能と再帰関数がどのように機能するかを理解します。コンパイラーがどのように機能するかを理解し、コンパイラーがどのように機能するかを理解すると、コンパイラーとして機能し、プログラムを非常に簡単にデバッグできます。
スタックの仕組みを説明しましょう。
最初に、関数がスタックにどのように格納されるかを知る必要があります。
ヒープストアの動的メモリ割り当て値。スタックストアの自動割り当てと削除の値。
例で理解しましょう:
def hello(x):
if x==1:
return "op"
else:
u=1
e=12
s=hello(x-1)
e+=1
print(s)
print(x)
u+=1
return e
hello(4)
このプログラムの一部を理解してください:
次に、スタックとは何か、スタックパーツとは何かを見てみましょう。
スタックの割り当て:
すべてのローカル変数をロードしたか、スタックからすぐに戻るものがスタックフレームになったとしても、関数が「戻る」場合、1つ覚えておいてください。これは、再帰関数がベース条件を取得し、ベース条件の後にreturnを置くと、ベース条件がプログラムの「その他」の部分にあるローカル変数をロードするのを待たずに、スタックから現在のフレームをすぐに返すことを意味します次のフレームがアクティベーションレコードにあることを返します。これを実際に見てください:
ブロックの割り当て解除:
そのため、関数がreturnステートメントを見つけるたびに、スタックから現在のフレームを削除します。
スタック値から戻ると、スタックで割り当てた順序と逆の順序で戻ります。
簡単にまとめました。たぶん誰かがより良い説明をしています。
呼び出しスタックは、1つまたは複数のスタックフレームで構成されます。各スタックフレームは、リターンでまだ終了していない関数またはプロシージャの呼び出しに対応しています。
スタックフレームを使用するために、スレッドは2つのポインターを保持します。1つはスタックポインター(SP)と呼ばれ、もう1つはフレームポインター(FP)と呼ばれます。 SPは常にスタックの「上部」を指し、FPは常にフレームの「上部」を指します。さらに、スレッドは、実行される次の命令を指すプログラムカウンター(PC)も保持します。
スタックには、ローカル変数と一時変数、現在の命令の実際のパラメーター(手順、関数など)が保存されます。
スタックのクリーニングに関しては、さまざまな呼び出し規約があります。
「コールスタックはスタックフレームで構成されています...」— Wikipedia
スタックフレームは、スタックに置くものです。それらは、呼び出すサブルーチンに関する情報を含むデータ構造です。
プログラマーは、スタックフレームについて広義ではなく(1つの関数呼び出しだけを処理し、戻りアドレス、引数、ローカル変数を保持するスタック内の単一のエンティティである)、狭い意味で質問がある場合があります-stack frames
コンパイラオプションのコンテキスト。
質問の著者がそれを意味したかどうかは関係ありませんが、コンパイラオプションの側面から見たスタックフレームの概念は非常に重要な問題であり、ここでの他の回答には含まれていません。
たとえば、Microsoft Visual Studio 2015 C/C++コンパイラには、stack frames
に関連する次のオプションがあります。
GCCには次のものがあります。
インテルC++コンパイラには次のものがあります。
次のエイリアスがあります。
Delphiには次のコマンドラインオプションがあります。
その特定の意味では、コンパイラーの観点から見ると、スタックフレームはルーチンのエントリおよび終了コードであり、アンカーをスタックにプッシュします。これは、デバッグおよび例外処理にも使用できます。 。デバッグツールは、スタックデータをスキャンし、スタック内のcall sites
を特定しながら、つまり、関数名を階層的に呼び出された順に表示するために、これらのアンカーをバックトレースに使用できます。 Intelアーキテクチャの場合、エントリはPush ebp; mov ebp, esp
またはenter
、終了はmov esp, ebp; pop ebp
またはleave
です。
そのため、コンパイラーオプションに関しては、プログラマーがスタックフレームの内容を理解することが非常に重要です。なぜなら、コンパイラーはこのコードを生成するかどうかを制御できるからです。
場合によっては、コンパイラによってスタックフレーム(ルーチンのエントリおよび終了コード)を省略できます。変数には、便利なベースポインター(BP /)ではなく、スタックポインター(SP/ESP/RSP)を介して直接アクセスします。 ESP/RSP)。スタックフレームの省略の条件、たとえば:
スタックフレーム(ルーチンのエントリおよび終了コード)を省略すると、コードが小さくなり、高速になりますが、スタック内のデータをバックトレースしてプログラマに表示するデバッガの機能に悪影響を与える可能性もあります。これらは、関数がどのような条件下でエントリと終了コードを持つべきかを決定するコンパイラオプションです。たとえば、(a)always、(b)never、(c)必要な場合(条件を指定)。