4

C ++ FAQ :

レジスタとスタックを持つ一般的なC++実装を想定すると、レジスタとパラメータはg()の呼び出しの直前にスタックに書き込まれ、次にパラメータはg()内のスタックから読み取られ、再度読み取られてレジスタが復元されます。 g()はf()に戻ります。

入れ子関数呼び出しに関して

void f()
{
  int x = /*...*/;
  int y = /*...*/;
  int z = /*...*/;
  ...code that uses x, y and z...
  g(x, y, z);
  ...more code that uses x, y and z...
}

1/レジスタとスタックを使用したC++の実装はすべてありますか?つまり、実装はコンパイラ/プロセッサ/コンピュータアーキテクチャに依存しますか?

2 /電話をかけるときの一連の指示(アセンブリ言語なし、全体像)とはf()何ですか?私はこのトピックに関するさまざまなことを読みました。また、言及された場所にレジスターがあることを覚えていませんが、スタックしているだけです。

3 /入れ子関数を扱うときに下線を引く追加の特異性/ポイントは何ですか?

ありがとう

4

2 に答える 2

3

数について2は、これはコンパイラやプラットフォームを含む多くのものに依存します。基本的に、引数を関数に渡したり返したりするさまざまな方法は、呼び出し規約と呼ばれます。x86プラットフォームでの呼び出し規約の記事では、操作のシーケンスについて詳しく説明しています。プラットフォームとコンパイラのこの小さな組み合わせだけで、いかに醜く複雑になるかがわかります。これが、さまざまなシナリオを聞いたことがある理由です。関数呼び出し規約のgen。プラットフォームを含む幅広いシナリオをカバーしてい64 bitますが、読みにくいです。gcc実際にはそうではないかもしれないので、それはさらに複雑にpushなりますpopスタックですが、スタックポインタを直接操作します。ここでのアセンブリではありますが、この例を見ることができます。呼び出し規約について一般化するのは難しいです。引数の数が十分に少ない場合、多くの呼び出し規約はatの使用stackをまったく回避でき、registers排他的に使用します。

番号3に関しては、入れ子関数は何も変更せず、次の関数呼び出しのために手順を繰り返すだけです。

数について1Seanが指摘した.Netように、バイトコードにコンパイルすると、スタック上ですべての操作が実行されます。CommonIntermediateLanguageのWikipediaページに良い例があります。

このx86-64 ABI ドキュメントは、特定の呼び出し規約がどのように機能するかを詳細に理解したい場合のもう1つの優れたドキュメントです。Figure 3.5 and 3.6多くのパラメーターを持つ関数の良い例と、general purpose registersfloating point registersの組み合わせを使用して各パラメーターがどのように渡されるかを示すので、きれいですstack。この種の素晴らしい図は、呼び出し規約をカバーするドキュメントを見るとめったに見つかりません。

于 2013-03-20T09:40:30.433 に答える
1

1. レジスタ/スタックの実装はC++コンパイラの最も一般的な基盤となる実装ですが、別のアーキテクチャを使用することを妨げるものは何もありません。たとえば、Javaバイトコードまたは.NETバイトコードを生成するコンパイラを作成できます。この場合、スタックベースのC++コンパイラが使用されます。

2.f()を呼び出す場合、一般的なアプローチは次のとおりです。

  • スタック上のリターンアドレスをプッシュし、f()にジャンプします

  • f()の場合:

  • ローカルのx、y、zにスペースを割り当てます。これは通常、スタックで実行されます。コールスタックに関するこの記事をご覧ください。

  • g(x、y、z)に到達すると、コンパイラはf()のスタックフレーム内の値にアクセスすることにより、値をスタックにプッシュするコードを生成します。C /C++はパラメータを右から左にプッシュすることに注意してください。

  • f()の最後に到達すると、コンパイラはreturn命令を挿入します。スタックの最上位には、戻るアドレスがあります(f()の呼び出しの前にプッシュされました)。

3.すべてが同じ基本テンプレートに従うため、ネストされた関数について特別なことは何もありません。

  • 関数を呼び出すには-パラメータをプッシュして関数を呼び出します。
  • 関数内-スタック内のローカル変数にスペースを割り当てます

これが一般的なアプローチです。コンパイラーは、パフォーマンスを向上させるために独自の最適化を導入します。たとえば、コンパイラは最初の2つのパラメータをレジスタに格納することを選択できます(たとえば)。

注:スタックを通過するパラメーターは、これまでで最も一般的なアプローチですが、他のアプローチもあります。詳細については、レジスタウィンドウに関するこの記事をご覧ください。

于 2013-03-20T09:39:39.717 に答える