web-dev-qa-db-ja.com

C / C ++での関数呼び出しのスタックフレームを理解していますか?

スタックフレームがどのように構築され、どの変数(params)がどの順序でスタックにプッシュされるかを理解しようとしていますか?一部の検索結果は、C/C++コンパイラーが関数内で実行される操作に基づいて決定することを示しました。たとえば、関数が渡されたint値を1だけインクリメントして(++演算子と同様)返す場合、関数のすべてのパラメーターとローカル変数をレジスターに入れます。

どのレジスターが戻り値または値渡しパラメーターに使用されるのかと思います。参照はどのように返されますか?コンパイラはどのようにeax、ebx、ecx、edxを選択しますか?

関数呼び出し中にレジスタ、スタック、ヒープ参照がどのように使用、構築、破棄されるかを理解するには、何を知る必要がありますか?

19
Gana

ダークが言ったことに加えて、スタックフレームの重要な用途は、レジスタの以前の値を保存して、関数呼び出しの後にそれらを復元できるようにすることです。したがって、レジスターがパラメーターの受け渡し、値の戻り、および戻りアドレスの保存に使用されるプロセッサーでも、それらのレジスターの値は、関数呼び出しの前にスタックに保存されるため、呼び出し後に復元できます。これにより、ある関数が独自のパラメーターを上書きしたり、独自の戻りアドレスを忘れたりすることなく、別の関数を呼び出すことができます。

したがって、一般的な「汎用」システムで関数Aから関数Bを呼び出すには、次の手順が必要になる場合があります。

  • 関数A:
    • 戻り値のスペースをプッシュ
    • プッシュパラメータ
    • 返品先住所をプッシュ
  • 関数Bにジャンプ
  • 関数B:
    • 前のスタックフレームのアドレスをプッシュする
    • この関数が使用するレジスタの値をプッシュします(復元できるようにするため)。
    • ローカル変数のスペースをプッシュ
    • 必要な計算を行う
    • レジスタを復元する
    • 前のスタックフレームを復元する
    • 関数の結果を保存する
    • 戻りアドレスにジャンプ
  • 関数A:
    • パラメータをポップします
    • 戻り値をポップする

これは、関数呼び出しが機能する唯一の方法ではありません(1つまたは2つの順序が乱れている可能性があります)が、スタックを使用して、入れ子になった関数呼び出しをプロセッサで処理する方法を理解できるはずです。

11
Caleb

これは、使用されている呼び出し規約に依存します。呼び出し規約を定義する人は誰でも、必要に応じてこの決定を行うことができます。

X86での最も一般的な呼び出し規約では、レジスターはパラメーターを渡すために使用されません。パラメータは、右端のパラメータからスタックにプッシュされます。戻り値はeaxに配置され、余分なスペースが必要な場合はedxを使用できます。参照とポインタは両方ともeaxのアドレスの形式で返されます。

11
Dirk Holsopple

スタックをよく理解していれば、メモリがプログラムでどのように機能するかを理解し、メモリがプログラムでどのように機能するかを理解すれば、関数がプログラムに格納される方法を理解し、関数がプログラムに格納される方法を理解すれば、再帰関数がどのように機能するかを理解できます。再帰関数がどのように機能するかを理解し、コンパイラーがどのように機能するかを理解し、コンパイラーがどのように機能するかを理解すると、コンパイラーとして機能し、プログラムを非常に簡単にデバッグできます。

スタックの仕組みを説明しましょう:

まず、関数をスタックに格納する方法を知っておく必要があります。

ヒープストアの動的メモリ割り当て値。スタックストアの自動割り当てと削除の値。

enter image description here

例で理解しましょう:

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)

このプログラムの一部を理解してください:

enter image description here

次に、スタックとは何か、スタックパーツとは何かを見てみましょう。

enter image description here

スタックの割り当て:

関数がすべてのローカル変数をロードしたか、スタックからすぐに返される何かがスタックフレームに関係なく「リターン」する場合、1つの点に注意してください。これは、再帰関数が基本条件を取得し、基本条件の後にreturnを配置することを意味します。基本条件は、プログラムの「else」部分にあるローカル変数のロードを待たず、スタックから現在のフレームをすぐに返します。戻り次のフレームはアクティブ化レコードにあります。これを実際に見てください:

enter image description here

ブロックの割り当て解除:

そのため、関数がreturnステートメントを見つけると、スタックから現在のフレームを削除します。

スタックから戻る間、値はスタックに割り当てられた順序とは逆の順序で戻ります。

enter image description here

これらは非常に短い説明であり、スタックと二重再帰についてさらに詳しく知りたい場合は、このブログの2つの投稿をお読みください。

段階的なスタックの詳細

スタックを使用した段階的な二重再帰の詳細

5
user5904928

あなたが探しているものは Application Binary Interface -ABIと呼ばれます。

ABIを詳しく説明するコンパイラごとに仕様があります。

各プラットフォームは通常、コンパイラー間の相互運用性をサポートするために、ABIを指定します。たとえば、 x86呼び出し規約 は、x86およびx86-64の一般的な呼び出し規約を詳しく説明しています。ただし、ウィキペディアよりも公式のドキュメントを期待しています。

3
Bill Door