15

次の関数が与えられた場合、各ローカル変数はスタック上で宣言されますか?

std::string reallyCoolFunction(unsigned int a)
{
   if( a < 20 ) 
   {
     std::string result1 = "This function is really cool";
     return result1;
   }

   if( a >=20 && a <= 40 )
   {
     std::string result2 = "This function is kind of cool";
     return result2;
   }

   if( a > 40 )
   {
     std::string result3 = "This function is moderately cool";
     return result3;
   }

 std::string result4 = "This function really isn't that cool"; 
 return result4; // remove warning

}

この状況では、std::string実際に必要なのは 1 つだけです。4 つすべてがスタックに割り当てられるのでしょうか、それとも 1 つだけが割り当てられるのでしょうか?

4

6 に答える 6

14

決定はコンパイラ次第です。自動変数は、次の変数がスコープに入る前にスコープ外になるため、コンパイラはそれらのメモリを再利用できます「スタック」変数は、実際には C++ 仕様に従って自動保存期間を持つ変数であるため、スタック上にまったく存在しない可能性があることに注意してください。

于 2013-01-22T15:17:34.587 に答える
10

ほとんどのコンパイラでは、1 つの文字列のみが割り当てられます。ただしstd::string、動的メモリを使用するため、そのコンテンツのほとんどがヒープに割り当てられることに注意してください。

于 2013-01-22T15:16:56.053 に答える
6

ほとんどの場合、0またはおそらく1(リリースの場合)および確かに4(デバッグの場合)です。

これはRVO:戻り値の最適化と呼ばれます。

コンパイラーは、実際にはコピーを完全に削除し、呼び出し元から提供されたスロットstd::stringに直接ビルドすることができます。これはABI固有であり、すべての最適化はいくつかの基準が満たされた場合にのみ適用されるためです。あなたの場合、それが適用される可能性があります。

確認したい場合は、変換/最適化パイプラインのさまざまな段階でコンパイラーの出力を熟読してみることができます。ツールチェーンによっては難しいかもしれません。

于 2013-01-22T15:47:53.380 に答える
2

コンパイラに依存します。
コンパイラが、必要な文字列が 1 つだけであると決定的に判断できるほどインテリジェントな場合は、1 つの文字列に対してのみコードを出力します。

コンパイラは十分にインテリジェントですか?

最も簡単な方法は、生成されたアセンブリ コードを確認することです。

4 つすべてがスタックに割り当てられますか、それとも 1 つだけが割り当てられますか?

1 文字列でも 4 文字列でも、文字列オブジェクトは関数に対してローカルなスタックに配置されますが、文字列のメモリは freestore に割り当てられます。

于 2013-01-22T15:17:22.327 に答える
0

この場合、コンパイラは 4 つ、1 つ、2 つ、または 3 つの変数を作成できます。しかし、私が知っているほとんどのコンパイラは、result4 が関数のスコープ全体にあるため、1 つまたは 2 つしか作成しません。

もちろん、「正しい」ことを行うと、コンパイラが混乱し、絶対に必要以上のことを行う可能性があるため、重要な機能でこれに依存することは特に良いことではありません.

編集: オブジェクトが実際に「使用」されている場合にのみ std::string のコンストラクターを実行する必要があることを追加する必要があるため、スタックスペースを使用することはできますが、コンストラクターを呼び出すべきではありません。これは、次のような場合に重要です。

void func()
{
    if (something)
    {
        Lock myLock(&global_lock_object);   // Constructor locks global_lock_object
        ... do stuff that needs global_lock_object locked ... 
        // end of scope runs destructor of Lock that unlocks global_lock_object. 
    }
    ... more code that takes a long time to execute but doesn't need lock. ...
}

ここで、コンストラクターLockが「早すぎる」ように実行され、同じスコープで破棄された場合 [対称である必要があります]、ロックは関数の全期間にわたって保持されますが、これは正しくありません。

于 2013-01-22T15:22:25.077 に答える
0

簡単な答え: アセンブラを見てください。

長い答え: コンパイラ静的チェックを適用して、4 つすべての変数が必要なのか、一部の変数だけが必要なのかを判断する場合があります。デバッグ モードで 4 つの異なる変数を割り当てるコンパイラもあれば、割り当てないコンパイラもあります。リリース モードでは、一部のオプティマイザーは、最初の 3 つがそれぞれ独自のスコープ内にあるため、同じ場所に配置できると見なす場合があります。したがって、これらのコンパイラは、スタック上に 2 つの文字列変数用のスペースを予約できます。4 番目の変数が最初の 3 つの変数と決して共存しないことを確認するには、もう少し分析する必要があります。そのため、一部のオプティマイザーは、残りの 2 つの変数も同じ場所に配置する可能性があります。

しかし、あなたのコンパイラがそれを行うかどうか、そしてコンパイラがもう少し複雑な状況でそれを行うかどうかは、出力を分析することによってのみ確実に判断できます。

于 2013-01-22T15:24:31.047 に答える