23

重複の可能性:
ヒープの代わりにスタックを使用するのが最適なのはいつですか。その逆も同様です。

ヒープとスタックに関する他のいくつかの質問を読みましたが、それらは、なぜそれらを使用するのかというよりも、ヒープ/スタックが何をするかに焦点を当てているようです。

スタック割り当ての方が高速であり(スタックポインタを移動するのではなく、ヒープ内の空き領域を探す)、使い終わったときに割り当てられたメモリを手動で解放する必要がないため、ほとんどの場合、スタック割り当てが優先されるように思われます。 。ヒープ割り当てを使用するために私が見ることができる唯一の理由は、関数内にオブジェクトを作成し、それをその関数スコープ外で使用したい場合です。これは、スタック割り当てメモリが関数から戻った後に自動的に割り当て解除されるためです。

私が気付いていないスタック割り当ての代わりにヒープ割り当てを使用する他の理由はありますか?

4

9 に答える 9

39

いくつかの理由があります:

  • malloc主なものは、ヒープ割り当てを使用すると、オブジェクトの存続期間( /callocからfree)を最も柔軟に制御できることです。
  • スタックスペースは通常、少なくともデフォルト構成では、ヒープスペースよりも限られたリソースです。
  • ヒープスペースの割り当ての失敗は適切に処理できますが、スタックスペースの不足は回復できないことがよくあります。

柔軟なオブジェクトの有効期間がなければ、バイナリツリーやリンクリストなどの有用なデータ構造を作成することは事実上不可能です。

于 2009-10-11T05:42:09.373 に答える
20
  1. 関数の呼び出しを超えて割り当てを存続させたい
  2. スタックスペースを節約したい(通常は数MBに制限されています)
  3. 再配置可能なメモリ(Win16、データベースなど)を使用しているか、割り当ての失敗から回復したいと考えています。
  4. 可変長なんでも。あなたはこれを偽造することができますが、あなたのコードは本当に厄介になります。

大きなものは#1です。あらゆる種類の並行性に入るとすぐに、またはIPC#1がいたるところにあります。ほとんどの重要なシングルスレッドアプリケーションでさえ、ヒープを割り当てずに考案するのは難しいです。これは、実際にはC /C++の関数型言語を偽造していることになります。

于 2009-10-11T05:50:52.197 に答える
10

というわけで紐を作りたいと思います。ヒープまたはスタックで作成できます。両方を試してみましょう:

char *heap = malloc(14);
if(heap == NULL)
  {
    // bad things happened!
  }
strcat(heap, "Hello, world!");

スタックの場合:

char stack[] = "Hello, world!";

これで、これら 2 つの文字列がそれぞれの場所に配置されました。後で、それらを長くしたいと思います:

char *tmp = realloc(heap, 20);
if(tmp == NULL)
  {
    // bad things happened!
  }
heap = tmp;
memmove(heap + 13, heap + 7);
memcpy(heap + 7, "cruel ", 6);

スタックの場合:

// umm... What?

これは 1 つの利点にすぎず、他の利点について言及している人もいますが、これはかなり優れた利点です。ヒープを使用すると、少なくとも割り当てられたスペースを大きくしようとすることができます。スタックを使用すると、私たちは持っているものにこだわっています。余地を増やしたい場合は、それをすべて前もって宣言する必要があります。

char username[MAX_BUF_SIZE];
于 2009-10-11T05:58:52.353 に答える
4

ヒープを使用する最も明白な理由は、関数を呼び出して、長さが不明なものを返す必要がある場合です。呼び出し元がメモリブロックとサイズを関数に渡す場合もありますが、それ以外の場合、特に返されるものが複雑な場合(たとえば、ポインタが飛び交うさまざまなオブジェクトのコレクションなど)、これは実用的ではありません。

于 2009-10-11T05:46:47.197 に答える
3

多くの場合、サイズ制限は大きな問題です。スタックは通常、低メガバイトまたはキロバイト (スタック上のすべてのもの) で測定されますが、最近のすべての PC では数ギガバイトのヒープを使用できます。したがって、大量のデータを使用する場合は、ヒープが絶対に必要です。

于 2009-10-11T06:09:58.710 に答える
0

オブジェクトの有効期間を手動で制御すること(あなたが言及した)に加えて、ヒープを使用するその他の理由には次のものがあります。

  • オブジェクトのサイズに対する実行時の制御 (プログラムの実行中の初期サイズと「後の」サイズの両方)。

たとえば、実行時にのみ認識される特定のサイズの配列を割り当てることができます。

C99 での VLA (Variable Length Arrays) の導入により、ヒープを使用せずに固定ランタイム サイズの配列を割り当てることが可能になりました (これは基本的に「alloca」機能の言語レベルの実装です)。ただし、C99 でもヒープが必要な場合もあります。

  • オブジェクトの総数に対する実行時の制御。

たとえば、バイナリ ツリー構造を構築する場合、ツリーのノードを事前に意味のある方法でスタックに割り当てることはできません。「オンデマンド」でそれらを割り当てるには、ヒープを使用する必要があります。

  • スタックスペースが限られているため、低レベルの技術的考慮事項(他の人はすでに言及しています)。

たとえば、大きな I/O バッファーが必要な場合は、短時間でも (単一の関数内で)、大きな自動配列を宣言する代わりに、ヒープから要求する方が理にかなっています。

于 2009-10-11T06:28:53.197 に答える
0

追加するだけで、allocaを使用してスタックにメモリを割り当てることができますが、スタック上のメモリは制限されており、スペースは関数の実行中にのみ存在します。これは、すべてをヒープに割り当てる必要があるという意味ではありません。すべての設計上の決定と同様に、これもやや困難です。両方の「賢明な」組み合わせを使用する必要があります。

于 2009-10-11T06:16:49.257 に答える
0

スタックとヒープは同じ「開いた」メモリ空間を共有しており、メモリのセグメント全体を使用する場合、最終的にそれらが一致するポイントに到達する必要があります。それぞれが使用するスペースのバランスを保つと、後でメモリの割り当てと割り当て解除のコストが償却され、漸近値が小さくなります。

于 2009-10-11T07:57:44.063 に答える
0

スタック変数 (「自動変数」と呼ばれることが多い) は、常に同じで、常に小さくしたい場合に最適です。

int x;
char foo[32];

すべてスタック割り当てです。これらもコンパイル時に修正されます。

ヒープを割り当てる最大の理由は、必要なスペースを常に把握できるとは限らないことです。これは、プログラムを実行して初めてわかることがよくあります。制限について考えているかもしれませんが、必要なスペースの正確な量だけを使用したいと思うでしょう。

1k から 50mb のファイルを読み込む必要がある場合、次のようにはしません。

int readdata ( FILE * f ) {
  char inputdata[50*1024*1025];
  ...
  return x;
}

これはスタックに 50MB を割り当てようとしますが、スタックは通常 256k に制限されているため、通常は失敗します。

于 2009-10-11T07:28:14.620 に答える