2016-11-15 10 views
2

ではありませんなぜ私は小さな構造体の動的配列を保持しなければならないCの構造体に苦しんだ:C・フリー・ダイナミック・アレイ - 彼らはcontigous

typedef struct issueStruct { 
    int data; 
} issue; 

typedef struct volumeStruct { 
    issue* collection; 
    size_t elements; 
} volume; 

私は動的にIできるだけ多くの問題の構造体を作成することができますボリューム構造体の配列内のようなものです。私はその配列を通して繰り返すこともできます:

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume)); 
    TimeMagazine->collection = (issue*)malloc(4 * sizeof(issue)); 
    TimeMagazine->elements = 4; 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+i;  // Advance ptr 
    } 
    return 0; 
} 

OUTPUT: 
[Linux]$ gcc -Wall magazines.c 
[Linux]$ ./a.out 
0) 0 
1) 100 
2) 200 
3) 300 
[Linux]$ 

これまでのところ、とても良いです。上記のGDBを踏むと、問題の構造体が連続したメモリアドレスを持たないように見えますが、すべてが大丈夫です。私が見たメモリアドレスは次のとおりです。

issue 0) 0x602030 
issue 1) 0x602034 
issue 2) 0x60203c 
issue 3) 0x602048 

これは私にいくつかの一時停止を与えました。私はすべての問題が4バイト離れていると仮定していたでしょう。sizeof(issue) = 4です。もっと真剣に、配列の要素を解放するために "反復スルー"コードを変更すると、私のコードはエラーになります。具体的には、2番目の問題を解決しようとすると失敗します。ここでは、コードがあります:

i = 0; 
    ptr = TimeMagazine->collection; 
    issue* ptr2 = ptr; 
    while(i< TimeMagazine->elements){ 
      printf("freeing %d...\n", i); 
      i++; 
      free(ptr2);   // free ptr2 
      ptr2 = ptr = ptr+i; // advance ptr & ptr2 
    } 

ここでエラー(Linux上のGCC)があります:

*** Error in `./a.out': free(): invalid pointer: 0x000000000137c034 *** 

だから私は、私はここで何かが欠けてると確信しているが、わからないものを。誰かが配列要素を解放する効果的な方法をお勧めしますか?

多くの感謝!

-Pete

PSは - ポスト「の配列に構造体を解放する」のがたくさんありますが、どれも正確に私がやっているものと一致するように見えたん。だから私はこの質問の私のバージョンがユニークであることを願ってこれを投稿しています。

+1

1つの 'malloc'、1つは' free'です。それがルールです。現時点では、 'collection'配列全体に対して' malloc'を1つだけ持っていて、配列内の各要素に対して1つの 'free'呼び出しを複数持つようにしようとします(ポインタ算術は以下の答えでは間違っています) 。 – kaylum

答えて

7
while(i < TimeMagazine->elements){ 
     ptr->data = 100*i; 
     printf("%d) %d\n", i, ptr->data); 
     i++; 
     ptr = ptr+i;  // Advance ptr 
} 

あなたは、ptr = ptr+iに間違ったポインタ演算を使用しているptr = ptr+1あるべきか、境界の外にアクセスします。 freeセクションと同じです。

とコメントして@kaylumで指摘したように:あなたは、ループ内のfreeを呼び出している、これも間違っている、あなたはfree(TimeMagazine->collection);一度にできますが、同じブロック内4要素のためのスペースを確保しているため。

+1

メモリ空き部分も同じです。 – Ari0nhh

+0

また、ポインタを出力するには、フォーマット ""%p "'を使用してください。 ''%d "'があなたの望むことをするという保証はありません。 – Gene

+0

@Gene、OPはgdbのアドレスを(プログラムではなく)印刷しているようですが、 'ptr-> data'は' int'です。 –

2

これは、動的配列を含む連続したメモリおよび構造体に関する副次的な注意です。実際の回答はthe answer given by @KeineLustを参照してください。

前述のように、malloc == one free

しかし、言及されていないことは、連続したメモリがキャッシングの考慮事項によりしばしば良く機能するということです。

これは、同じコールを使用してメモリとダイナミックアレイが割り当てられている場合、struct volumeStructの方がパフォーマンスが向上することを意味します。

これを実行する一般的な方法は2つあります。

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue))); 
    TimeMagazine->collection = TimeMagazine + 1; // pointer arithmetics 
    TimeMagazine->elements = 4; 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+1;  // Advance ptr 
    } 

    free(TimeMagazine); 

    return 0; 
} 

別のオプション(私はこれはC99で導入されたと思います。(私はptr = ptr + 1を持っているあなたのループを固定し、私たちが範囲外に行かない)あなたが現在持っている同じ構造を使用して

一つ、 )は、構造体の最後に可変長配列を追加することです。これにより、collectionポインターに必要な8(または4)バイトが節約されます。

すなわち:

typedef struct issueStruct { 
    int data; 
} issue; 

typedef struct volumeStruct { 
    size_t elements; 
    issue collection[]; 
} volume; 

int main(){ 
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue))); 
    TimeMagazine->elements = 4; 
    // no need to assign a value for TimeMagazine->collection 

    issue* ptr = TimeMagazine->collection; 
    int i; 

    // Populate & iterate through array: 
    i = 0; 
    while(i < TimeMagazine->elements){ 
      ptr->data = 100*i; 
      printf("%d) %d\n", i, ptr->data); 
      i++; 
      ptr = ptr+1;  // Advance ptr 
    } 

    free(TimeMagazine); 

    return 0; 
} 

の大きな利点は、CPUのメモリキャッシュとシンプルなコードです。オブジェクトごとに2つのシステムコール(1つのmallocと1つのfree)を保存しているという事実は、ほとんどの場合無関係です。

+0

これは絶対に華麗です。私を混乱させる1つの事柄...実際には、問題構造体のmalloc()呼び出しはありません。私は、問題のmalloc()が "TimeMagazine"ボリューム構造体のmalloc()コールに折りたたまれていることを理解しています。それが問題の記憶を切り開いています。次に、問題* ptrはTimeMagazineのコレクションを指しています。したがって、ptrを+1すると、コンパイラは "+1"が実際に "+ sizeof(issue)"を意味することを知っていますか? – Pete

+0

@Pete、はい、コンパイラは 'sizeof(* ptr)== sizeof(issue)'を知っていて、 'ptr + = 1'を使うと正しいポインタ算術演算を実行します。ポインタは、(しばしば仮想の)メモリアドレスを表す数字に過ぎない。インメモリ型のデータはありません。したがって、型は常にポインタ演算を調整するコンパイラによって常に制御されます。 – Myst

+0

うわー、ありがとう。私は多くのドキュメントでCのこの側面について読んだことがありますが、慣れるまでは奇妙なことです。再度、感謝します! – Pete