6

これが以前に尋ねられた場合は申し訳ありませんが、何も見つかりませんでした...

「通常の」x86 アーキテクチャの場合:

C++ で大きな関数を呼び出すと、メモリはすべてのスタック変数にすぐに割り当てられますか? または、関数が終了していなくてもスタックサイズを変更できる (および変更する) コンパイラがあります。

たとえば、新しいスコープが開始された場合:

int largeFunction(){
    int a = 1;
    int b = 2;

    // .... long code ....

    { // new scope

         int c = 5;

         // .... code again ....

    }

    // .....

}

別のスコープの先頭にある変数 c についても呼び出しスタックが「成長」し、最後に「縮小」する可能性はありますか? それとも、現在のコンパイラは、関数のエントリと戻り値でスタック ポインタに影響を与えるコードを常に生成しますか? 事前にご回答いただきありがとうございます。

4

3 に答える 3

8

1) 関数の長さは、スタックやヒープに関係なく、メモリの割り当てとは関係ありません。

2) スタックがいつ「割り当てられる」かは、コンパイラが最も効率的なコードを作成する方法にのみ依存します。「効率的」には幅広い要件があります。すべてのコンパイラには、速度とサイズのオプティマイザの目標を変更するオプションがあり、ほとんどのコンパイラは、スタック消費量やその他のパラメータを削減するためにも最適化できます。

3) 自動変数はスタックに入れることができますが、必須ではありません。多くの変数を CPU のレジスタに「割り当てる」必要があります。これにより、コードが大幅に高速化され、スタックが節約されます。ただし、これは CPU プラットフォームに大きく依存します。

4) コンパイラが新しいスタック フレームをいつ生成するかは、コードの最適化の問題でもあります。これによりリソースが節約されるか、アーキテクチャにより適している場合、コンパイラは「順不同の実行」を行うことができます。したがって、いつスタック フレームが使用されるようになるかという質問には答えられません。新しいスコープ (左中かっこ) は、新しいスタック フレームを割り当てるためのポイントになる可能性がありますが、これは決して保証されません。実際のスコープから呼び出されたすべての関数のすべてのスタック相対アドレスを再計算するのは効率的でない場合があります。

5) 一部のコンパイラは、自動変数にヒープ メモリを使用することもできます。これは、特別な命令によるアクセスがスタック相対アドレッシングとして高速な場合に、組み込みコアでよく見られます。

しかし、通常、コンパイラがいつ何をしたいかはあまり重要ではありません。覚えておくべき唯一のことは、スタックが十分に大きいことを保証する必要があるということです。多くの場合、新しいスレッドのシステム コールには、スタック サイズを設定するパラメーターがあります。そのため、実装に必要なスタック サイズの数を知る必要があります。しかし、他のすべての場合: 考えるのを忘れてください。この仕事は、コンパイラ開発者によって完全に行われます。

于 2013-10-10T17:24:44.740 に答える
3

答えはわかりません (有効なプログラムは違いを見分けることができないため、興味があるから知りたいだけだと思います) が、このような関数を呼び出すことで、コンパイラの動作をテストできます。新しいスコープの前と新しいスコープの後:

std::intptr_t stackaddr()
{
    int i;
    return reinterpret_cast<std::intptr_t>(&i);
}

同じ結果が得られた場合は、スタックが作成前にすでに調整されていることを意味しますc

cG++ 4.7 に変更があり、スコープが終了した後のスタック領域をコンパイラが再利用できるようになりました。以前は、その時点以降の新しい変数によってスタックの使用量が増加していました。 「G++ は、割り当てられたスタック領域を適切に再利用するようになりました。これにより、一部の C++ 関数のスタック消費を大幅に削減できます。」しかし、それはいつ/どこで予約されるかではなく、関数へのエントリ時に予約されるスタックの量にのみ影響すると思います。

于 2013-10-10T17:30:46.290 に答える
0

これは、使用しているシステムのランタイム規則に完全に依存しますが、CPU アーキテクチャは通常、どのスタック管理を安全に使用できるかを定義するため、決定において大きな役割を果たします。たとえば、MacOS X の下の古い PowerPC では、スタック フレームは常に固定サイズであり、新しいスタック フレームのローエンドにあるスタック ポインターの 1 つのアトミック ストアがそれを割り当て、スタック ポインターを逆参照することは、スタック フレーム全体をポップすることと同等でした。

Linux や (間違っていたら訂正してください) x86 上の Windows などの現在のシステムには、アトミック プッシュおよびポップ命令 (PowerPC にはアトミック ポップはありません) を使用したより動的なアプローチがあり、関数呼び出しのパラメーターが各関数呼び出しの前にスタックし、割り当てられたスタック フレームのサイズを毎回効果的に変更します。

したがって、はい、現在の多くのシステムでは、コンパイラはスタック フレームのサイズを変更できますが、他のシステムでは、そのような操作を実行するのは少なくとも困難です (決して不可能ではありません)。

于 2013-10-10T17:34:43.003 に答える