2016-05-26 13 views
5

私は主にの生存率に興味があり、そのような配列を縮小しています。ダイナミックに割り当てられた2D配列でrealloc()を使用するのは良い考えですか?

私は個々の適度に大きな2D配列を作成するために、単一のmalloc()呼び出しを使用したプロジェクトに取り組んでいます。 (それぞれ最大で数十MiBです。)配列の1つのライフにわたって、その内容は劇的に(半分以上)縮小します。明らかに、私はプログラムの寿命のために配列の大きさだけを残すことができます。 (GiBのRAMが利用可能なシステムでは、x MiBしか使用できません)しかし、私たちは、プログラムが終了する前に割り当てられたスペースの半分以上が使用不能になっていると話しています。配列を使用すると、生き残っているすべてのデータは連続した行セット(ブロックの先頭)に保持されます。もし私が本当にそれを必要としなければ、すべてのRAMを保持することは無駄に思えます。

動的に作成された配列を縮小するためにrealloc()を使うことができますが、2D配列はもっと複雑です。私はそれを構成する関数を実装したので、そのメモリのレイアウトを理解していると思うが、これは言語の理解とコンパイラの動作の限界を押し進めている。明らかに、私は行だけでなく行ポインタを扱わなければならないでしょうが、これはすべての結果がどのように予測できるかわかりません。

そして、はい、私は単一のmalloc()で配列を作成する必要があります。問題のオブジェクトには、数百万行があります。私は別々に各行をmalloc()するループを使用しようとしましたが、プログラムは常に約100,000のmalloc()で凍っていました。

char ** alloc_2d_arr(int cnum, int rnum) { 
     /* ((bytes for row pointers + (bytes for data)) */ 
     char **mtx = malloc(rnum * sizeof (char *) + rnum * cnum * sizeof (char)); 

     /* Initialize each row pointer to the first cell of its row */ 
     char *p = (char *) (mtx + rnum); 
     for (int i = 0; i < rnum; i++) { 
       mtx[i] = p + i * cnum; 
     } 

     return mtx; 
} 
+2

'realloc'はこのような大きなテーブルのためには良いアイデアではなく、" avl "と" red-black "ツリーを見てください。 –

+1

"本当に必要ない場合は、すべてのRAMを保持するのは無駄なようです。" - 最初の*プロフィール*。第二に、reallocは、すべてのあなたの内部のデータを別のページにコピーするトリガーとなる高いリスクを冒しています。ラムを保存しようとする理由だけで発生する些細な費用はあなたが本当に問題ではないと主張します。ここでの唯一の勝利シナリオは、メモリベースと同じリージョンヘッドを保持する 'realloc 'であり、他の用途のためにテールページが返されます。何か 'realloc'は保証がありません.... – WhozCraig

+0

...代わりに、2つ(または3つか4つ)の割り当てを考えて、あなたが最終的にどのものを保持しているかを覚えておいてください。' free() ' - 一度イベントが起これば、もはや必要のないものを送りますか?私。あなたの行列の "保存された"半分が最初の割り当てにあり、後半は別の割り当てにあり、最終的には後半だけ解放します。 – WhozCraig

答えて

2

多次元配列を使用し、これはまたは可変長配列へのポインタなしに行うことができる次のよう

は背景については、私は、これらの配列を構築するために使用しているソースです。あなたはおそらく追加のメモリを割り当てたくないので、これはその場で行われます。

まず20 10で配列の割り当て:あなたが行数を変更したい場合は、何の要素を移動する必要がない

int (*array)[10] = malloc(sizeof(int) * 20 * 10); 
for(size_t i = 0 ; i < 20 ; i++) 
    for(size_t j = 0 ; j < 10 ; j++) 
      array[i][j] = i * 100 + j; 

を、唯一 のreallocが必要とされています。行数を15に変更することは簡単です。

array = realloc(array , sizeof(int) * 15 * 10); 

列数を変更する場合は、要素を移動する必要があります。最初の列をコピーする必要はないので、2番目の列からコピーが開始されます。関数memmoveは、この場合には起こり得ないメモリの重なりを避けるために使用されますが、新しいカラム数が大きかった場合に起こります。また、エイリアシングの問題を回避します。このコードは、割り当てられたメモリを使用しているためにのみ定義されています。

int (*newarray)[3] = (int(*)[3])array; 
for(size_t j = 1 ; j < 15 ; j++) 
    memmove(newarray[j] , array[j] , sizeof(int) * 3); 
newarray = realloc(array , sizeof(int) * 15 * 3); 

の作業例:https://ideone.com/JMdJO0

新しい列カウントが古いものよりも大きくなった場合、その後、メモリは最初に再割り当てする必要があります(単に取得するのは3まで数える列を変更してみましょうより多くのスペース)、最後の列から開始して列のコピーが行われます。

+0

私はそれを認めてくれるのですが、 'int(* array)[10] = malloc(...);'を理解するのに問題があります。私の明らかに不十分なCの把握に基づいて、それは識別子として逆参照されたポインタを持つ新しく作成された変数を初期化するように見えます。トリプルポインタ(2回)を逆参照してmalloc()の出力を与えるのは1つのことですが、タイプを前に置くと、RAMアドレスがシンボルとして使用されているように見えます(意味がありません) 。私はC言語を学んでいたときに何があったのか見たような気がしますが、関連用語をグーグル化すると明らかに汚染された結果になります。 – CircleSquared

+0

@CircleSquared多次元配列へのポインタです。私の例では二次元。このように:int a [7] [9]; int(* pa)[9] = a; '、私の例ではポインタを自動配列に向けるのではなく、メモリを割り当てます。 – 2501

+0

多次元配列を動的に割り当てる**より効率的な方法を**私の質問に単に答えるだけでなく、私に見せてくれてありがとう。 *これで十分ですが、これは配列を設定するより良い方法です。* – CircleSquared

関連する問題