2013-01-22 10 views
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が実際に必要とされる、すべての4はスタックに割り当てますか、または唯一の1が割り当てられますでしょうか?

答えて

14

決定はコンパイラ次第です:自動変数がスコープの外に出るので、次のものがスコープ内に来る前に、そのメモリを再利用することができますコンパイラ。 「スタック」変数は、実際にはの変数であり、C++仕様では自動保存期間がであるため、スタックにはまったく格納されていない可能性があります。

+0

私の答えには、あなたが言及することを忘れているという重要なビットがあると信じています。文字列はスタックに割り当てられません。私はそれが言及する価値があると思います –

+0

@IvayloStrandjev絶対に - 私は非常に理由であなたの答えをupvoted :) – dasblinkenlight

+0

自動ストレージはスタックの動作を保証しています。それが「スタック」と呼ばれるものです。 「実際には」実際にはかなり無礼な言葉です。自動変数は、as-ifルールの下で最適化されてスタックに割り当てられることを避けることができます。そして、*スタックに割り当てられているかのように*なります。したがって、大きな注意を払うほど意味がありません。 –

10

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

+5

文字列*は動的メモリを使用することがありますが、必ずしもそうである必要はありません。たとえば、MSVCの標準ライブラリの小さな文字列の最適化を参照してください。 –

+0

@ArneMertzああ、わかりませんでした。ありがとう:) –

2

コンパイラによって異なります。
コンパイラが、1つの文字列だけが必要であることを決定的に判断するのに十分なインテリジェントな場合、1つの文字列に対してのみコードを出力します。

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

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

4つはスタックに割り当てられますか、1つしか割り当てられませんか?

文字列オブジェクトは関数のローカルスタックにありますが、文字列のメモリはfreestoreに割り当てられます。

+0

* 1または4の文字列であれば、文字列オブジェクトは関数のローカルスタックにありますが、文字列のメモリはフリーストアに割り当てられます* SSO:Short String Optimization(DirkumwareとlibC++で実装されています) –

+0

@MatthieuM .: True&agree。修正していただきありがとうございます。厳密に言えば、これは実装の詳細です。標準では、ストレージが行われる必要があるところでは、 'std :: string'の実装が遵守しなければならない特定の観測可能な振る舞いのみを要求します。 –

+1

はい、C++ 03とC++ 11の間で注目すべき点は1つですが、gccの最適化(Write On Write)は新しい制約のためにもう不可能です。 –

0

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

"正しい"ことをすれば、コンパイラは混乱するかもしれませんし、絶対に必要以上に機能するので、重要な機能にこれを頼ることは特に良いことではありません。

編集:std :: stringのコンストラクタは、オブジェクトが実際に "使用されている"場合にのみ実行する必要があるため、スタックスペースを使用することがありますが、コンストラクタを呼び出すべきではありません。あなたはこのような何か場合、これは重要です:Lockのコンストラクタは、「早すぎる」を実行し、[それは対称的でなければなりません]と同じ範囲で破壊された場合、今

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. ... 
} 

を、ロックが開催されます関数の全期間にわたって、間違っている可能性があります。

0

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

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

しかしあなたコンパイラがあることありませんか、と彼はまだあなたが出力に含まを分析する場合にはわずかに、より複雑な状況では、唯一確実に判定することができることを行いますか。

6

おそらくまたは多分(リリース中)と確かに(デバッグ中)。

これは、RVO:Return Value Optimizationと呼ばれます。

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

チェックしたい場合は、翻訳/最適化パイプラインのさまざまな段階でコンパイラの出力を調べることができます。あなたのツールチェーンにもよるが、それは難しいかもしれない。

+0

「もしかすると勝つためのルール」。 – dmckee