質問

静的配列よりもmalloc(失敗時のNULLリターン以外)を使用する利点は何ですか?次のプログラムは、すべてのRAMを使い果たし、ループのコメントが解除されている場合にのみスワップを埋め始めます。クラッシュしません。

...

#include <stdio.h>

unsigned int bigint[ 1u << 29 - 1 ];
unsigned char bigchar[ 1u << 31 - 1 ];

int main (int argc, char **argv) {
  int i;
/*   for (i = 0; i < 1u << 29 - 1; i++) bigint[i] = i; */
/*   for (i = 0; i < 1u << 31 - 1; i++) bigchar[i] = i & 0xFF; */

  getchar();
  return 0;
}

...

いくつかの試行錯誤の後、上記がGCC 4.3を搭載した32ビットIntelマシンで許可される最大の静的配列であることがわかりました。これは標準の制限、コンパイラの制限、またはマシンの制限ですか?どうやら私はそれらの多くを望むだけ持つことができます。それはセグメンテーション違反になりますが、とにかくmallocよりも多くを要求する(そして使用しようとする)場合のみです。

静的配列が実際に割り当てられ、安全に使用されているかどうかを判断する方法はありますか?

編集:ヒープを管理するために仮想メモリシステムに処理させる代わりにmallocを使用する理由に興味があります。どうやら必要なサイズの何倍にもアレイのサイズを変更でき、仮想メモリシステムは必要なものだけをRAMに保持するようです。たとえば、これらの巨大な配列の終わり(または始まり)では、プログラムは物理メモリを使用しません。さらに、すべての場所に書き込むことができる場合、ヒープ内のポインターをインクリメントするか、同じプロセスで以前の割り当てを検索する以外に、mallocは何をしますか?

編集者のメモ: 1&lt;&lt; 31 intの場合、未定義の動作が発生します 32ビットなので、質問を変更して 1u を読みました。質問の目的は、大きな静的バッファの割り当てについて尋ねることです。

役に立ちましたか?

解決

さて、本当に2つの理由から:

  1. 一部のシステムでは仮想メモリ管理が行われないため、移植性があるため。

  2. この配列を有用にするために、この配列を小さなチャンクに分割し、すべてのチャンクを追跡し、最終的に「解放」を開始する必要があります。不要になった配列のチャンクの一部では、メモリの断片化の問題が発生します。

全体として、移植性の利点なしに、多くのメモリ管理機能を実装することになります(実際には、mallocをほぼ再実装します)。

その理由:

  • メモリ管理のカプセル化と標準化によるコードの移植性。

  • コードの再利用による個人の生産性の向上。

他のヒント

mallocを使用すると、配列を拡大および縮小できます。配列は動的になるため、必要なものに正確に割り当てることができます。

これはカスタムメモリ管理と呼ばれます。 それはできますが、そのメモリの塊を自分で管理する必要があります。 このチャンクを使って独自のmalloc()を作成することになります。

に関して:

  

いくつかの試行錯誤の後、私は見つけました   上記は最大の静的配列です   32ビットIntelマシンで許可   GCC 4.3で。これは標準ですか   制限、コンパイラの制限、またはマシン   制限?

1つの上限は、4GB(32ビット)の仮想アドレス空間がユーザー空間とカーネル空間の間でどのように分割されるかに依存します。 Linuxの場合、最も一般的なパーティションスキームには、ユーザースペース用に3 GBのアドレス範囲とカーネルスペース用に1 GBのアドレス範囲があります。パーティションはカーネルのビルド時に構成可能で、2GB / 2GBと1GB / 3GBの分割も使用されています。実行可能ファイルがロードされるとき、バックアップするために実メモリが割り当てられているかどうかにかかわらず、すべてのオブジェクトに仮想アドレス空間を割り当てる必要があります。

1つのコンテキストでその巨大な配列を割り当てることはできますが、他のコンテキストではできません。たとえば、配列が構造体のメンバーであり、構造体を渡したい場合。一部の環境では、構造体のサイズに32Kの制限があります。

前述のように、メモリのサイズを変更して、必要なものだけを使用することもできます。パフォーマンスが重要なコンテキストでは、回避できる場合は仮想メモリにページアウトしないことが重要です。

スコープ外に出る以外にスタックの割り当てを解放する方法はありません。したがって、実際にグローバル割り当てを使用し、VMが実際のハードメモリを割り当てる必要がある場合、割り当てられ、プログラムが実行されるまでそこにとどまります。これは、仮想メモリの使用でのみプロセスが成長することを意味します(関数にはローカルスタックが割り当てられ、それらは「解放」されます)。

「維持」することはできません。スタックメモリが機能範囲外になると、常に解放されます。そのため、コンパイル時に使用するメモリ量を知る必要があります。

次に、あなたが持つことができるint foo [1&lt;&lt; 29]の数を決定します。最初のものはメモリ全体(32ビット)を占有し、(0x000000になります)2番目のものは0xffffffffまたはthereaoboutに解決されます。それから、3番目のものは何に解決しますか? 32ビットポインターでは表現できないもの。 (スタック予約は、コンパイル時に、部分的に実行時に、オフセットを介して、この変数またはその変数を割り当てるときにスタックオフセットがどれだけプッシュされるかを思い出してください。)

したがって、答えは、int foo [1&lt;&lt; 29]を取得すると、他のローカルスタック変数を使用して適切な深さの関数を作成できなくなるということです。

自分が何をしているのかわからない限り、実際にこれを行うことは避けてください。必要なだけのメモリを要求するようにしてください。使用されていなかったり、他のプログラムの邪魔になっていなくても、プロセス自体を台無しにする可能性があります。これには2つの理由があります。まず、特定のシステム、特に32ビットシステムでは、まれな状況でアドレス空間が早期に使い果たされる可能性があります。さらに、多くのカーネルには、予約済み/仮想/未使用メモリに関するプロセスごとの制限があります。プログラムが実行時にメモリを要求する場合、カーネルはこの制限を超えるメモリの予約を要求するとプロセスを強制終了できます。数MBしか使用せずにGBのメモリを予約しているため、mallocの失敗によりクラッシュまたは終了したプログラムを見てきました。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top