5

次の関数を検討してください。

int main()
{
    //statement(s);
    func1();
    //statement(s);
}

void func1()
{
    //statement(s);
    func2();
    //statement(s);
}

void func2()
{
    //statement(s);
}

func2コンパイラは、すべての操作を実行した後、どこに戻るかをどのように知るのですか?制御が関数func1(および正確にどのステートメント)に転送されることは知っていますが、コンパイラーはどのようにしてそれを認識しますか?コンパイラにどこに戻るかを指示するものは何ですか?

4

4 に答える 4

15

これは通常、コールスタックを使用して実装されます。

  • 制御が関数に移されるとき、戻るアドレスはスタックにプッシュされます。
  • 関数が終了すると、アドレスがスタックからポップされ、制御を呼び出し先に戻すために使用されます。

詳細は通常、コードがコンパイルされるハードウェアアーキテクチャによって義務付けられています。

于 2013-03-07T14:58:28.173 に答える
3

実際には、コンパイラはコードを実行しませんが、マシンは実行し、新しい関数を呼び出すと、スタックで現在呼び出されている関数の後に実行される次の命令のアドレスを格納します。戻ったら、命令ポインター(IP)にポップオフして、そこから再開できます。

説明のために少し簡略化しました。

于 2013-03-07T15:00:14.893 に答える
2

関数が呼び出されると、呼び出し元の関数の正しいリターンアドレスがどこかに配置されます。通常、標準ではスタックが義務付けられていませんが、これはリターンアドレスを格納する目的で正確に使用されます。

呼び出し規約が、何か問題が発生しない限り(たとえば、スタックオーバーフロー)、呼び出された関数が呼び出し元の関数に戻る方法を知っていることを確認するのは、コンパイラの義務です。

于 2013-03-07T14:58:46.757 に答える
1

ランタイムは、「呼び出しスタック」と呼ばれるものを利用します。これは、基本的に、呼び出されている関数が返された後に呼び出す次のステートメントのアドレスを保持します。したがって、関数呼び出しが行われ、制御が新しい命令アドレスにジャンプする前に、呼び出し元の関数の次の命令アドレスがスタックにプッシュされます。そして、このプロセスは、関数への後続の呼び出しごとに繰り返されます。では、なぜスタックだけなのですか?中断したところに戻る必要があるためです。これは基本的に「後入れ先出し」の動作であり、スタックはそれを行うデータ構造です。Visual Studioでプログラムをデバッグしているときに、この呼び出しスタックを実際に確認できます。「呼び出しスタック」と呼ばれる別のウィンドウがあり、呼び出しスタックに配置されたアドレスのエントリが表示されます。

于 2013-03-07T15:06:39.050 に答える