2010-12-07 13 views
4

素朴な疑問が...自動/静的なメモリ割り当て

を確認または拒否:

自動および静的記憶域期間のオブジェクト/変数のためのメモリの存在が決定されますコンパイル時には、自動オブジェクトに十分なメモリがないため、プログラムがランタイムに失敗する可能性は全くありません。

当然ながら、自動オブジェクトのコンストラクタが動的割り当てを実行し、そのような割り当てが失敗すると、これは自動割り当てではなく動的割り当ての失敗とみなされます。

+0

プログラムロジック。 – ruslik

+0

この質問の仕方は、宿題や試験の質問のように聞こえるが、私は8Kの担当者が宿題をするように依頼しているとは思えない。 :)私はそれがどこから来たのか不思議です。 –

+0

@R ..このようになりました。大きすぎる自動配列を割り当てるとコンパイルエラーが発生しますが、大きな動的配列を割り当てようとするとランタイムエラーが発生します。コンパイルが成功しても前者の場合はランタイムエラーが発生する可能性があります:) –

答えて

13

2つの単語:Stack Overflow。 :P

+1

私はこの答えに2つのアップフォートがあるとは信じられませんでした。そして私はそれを3つ作った。 –

+2

リンクを逃してはいけません。 '[:P]' –

+0

@Parasoon:私は笑っていませんでした。 –

14

自動割り当ては確実に失敗する可能性があります。これは、通常、スタックオーバーフローと呼ばれます。誰かがローカル変数として多様な大規模配列を持つことを試みると、これはかなり頻繁に表示されます。無制限(または制限されていない)再帰もこの原因になります。

実際にプラットフォームに依存しない方法では、自動割り当ての失敗を検出して処理できません。

+3

+1は、スタックオーバーフローの一般的な原因を指摘します。 –

+0

「自動割り当ての失敗を検出して処理する」。これには、ポータブルに予測できないことが含まれます。プログラム 'int main(){int a = 1; int b = 2;これは正式には動作が変化しないことを除いて実装(特に、2つのintに対して十分なスタックが利用可能であること)が異なるため、厳密には適合しません。問題は全く言及していません。その代わりに、プログラムの動作の有無、実行時の途中での中断、ヒープの破棄など、標準の範囲を超えています。 –

2

自動割り当てによってスタックオーバーフローが発生する可能性があります。これは、私が認識しているほとんどのアーキテクチャ/プラットフォームで即座にプロセスが終了する原因となります。

また、プログラムは静的変数のための十分な領域を基底プラットフォームから割り当てることはできません。その場合でもプログラムは失敗しますが、mainが呼び出される前に失敗します。

+0

あなたが幸運なら、それは即座に終了する。あなたが不運な場合、それはメモリの他の部分をぶつかり、攻撃者のルートを与えます。 :-)幸いにも、後者は実際には非常に大きな(多くのkb)オブジェクトをスタックに割り当てるだけですが、C99 VLAを安全に使用しないとこの問題が発生する可能性があります。 –

+0

@R。うーん....他のプラットフォームがこれをどのように処理しているかわからない。 Win32では少なくともプロセスを終了します。他のボックスには表示されません。 –

+0

@Billy:呼び出しごとにスタック使用量が数kb増加するだけで、ガードページが表示され、プロセスが終了します。しかし、1回の呼び出しでスタックが2GB増加するとどうなりますか?コンパイラがこれをチェックする特別なコードを生成し、それを必要としないすべての正常な機能を遅くしない限り、スタックポインタは他のメモリの途中で終了し、関数がうまくそのメモリを破壊します。 –

0

シンプルな反例:

#include <string.h> 

int main() 
{ 
    int huge[0x1FFFFFFF]; // Specific size doesn't matter; 
          // it just has to be bigger than the stack. 

    memset(huge, 0, sizeof(huge)/sizeof(int)); 

    return 0; 
} 
+0

通常、スタックフレームのサイズは無制限です。スタック自体の合計サイズは、通常は制限要因です。 –

+0

これはコンパイルされませんか? –

+0

@アーメン:そうです。私はそれを正確にコンパイルするものに変更します... – nmichaels

-1

例:

オーバーコミットしたシステムで
#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    A() { p = new int[0xFFFFFFFF]; } 

private: 
    int* p; 
}; 

static A g_a; 

int main() 
{ 
    cout << "Why do I never get called?" << endl; 
} 
+1

あなたはOPの質問の最後の文を忘れました。 "当然のことながら、自動オブジェクトのコンストラクタが動的割り当てを実行し、そのような割り当てが失敗すると、これは自動割り当てではなく動的割り当てでの失敗とみなされます。編集:と '名前空間の標準を使用して、'死ぬ必要があります! :P –

+0

'using namespace std;'は簡単な例ではほとんど問題になりません。この例はどこでも 'std ::'を持つよりも読みやすくなります。確かに、この例では、私は1つの 'cout'文を使用しているので、それは本当に重要ではありません(そして、その前にエラーが発生してから決して実行されません)。さらに、彼の質問は静的変数の作成に関するものでした。私は 'new'を使いましたが、メンバ変数を' int p [0x7FFFFFFF] 'として簡単に作成して同じ効果を得ることもできます。どちらの場合も、スタックオーバーフローが発生します。 –

+0

@ Zac:1.私はそれがコード* less *を読めるようにすると思います。標準ライブラリを呼び出すときは、コールサイトで明らかにする必要があります。 2.いいえ、メンバ変数も 'new'への呼び出しもスタックオーバーフローを起こしません。最初のケースは、静的なストレージスペースがなくなるため、おそらく失敗します。ヒープが使い尽くされたため、 'new'への呼び出しは失敗します。どちらの割り当てもスタックには触れません。これまで –

4

(例えばLinuxはデフォルトの設定では)、それはにつながるための静的記憶域期間のオブジェクトに対しても可能です実行時の失敗。プログラムの起動時に、これらのオブジェクトは、コピーオンライトゼロページ(初期化されていない場合)またはディスク上の実行可能ファイルのコピーオンライトマッピングのいずれかに存在します。最初に書き込みを試みると、ページフォールトが発生し、カーネルはプロセスのためにローカルで変更可能なコピーを作成します。カーネルが不注意でプロセスにコミットするほどのメモリを確保しなかった場合、これは失敗する可能性があり、その結果は恐ろしいOOM-killerになります。

ませ堅牢なシステムは、この問題を持っていない、とLinuxの動作がで固定することができます。それが依存するので、それが自動のもののために行うことはできません、静的オブジェクトのコンパイル時に確認することができますが

echo "2" > /proc/sys/vm/overcommit_memory 
+1

+1:Linuxによってあなたにもたらされる非標準的な振る舞い!デフォルト設定を使用して準拠したC実装を作成することを不可能にしていただき、ありがとうございます。 * BillはBSDをインストールします。 –

+0

私はovercommitも伝統的なBSDの振る舞いであると確信していますが、私は間違っているかもしれません。私はちょうど別のBSDがコメントするのに十分だとは知らないので、私はそれらについて言及しなかった。 –

関連する問題