サイズが可変なデータ構造の実現方法について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
bonbo

サイズが可変なデータ構造の実現方法について

#1

投稿記事 by bonbo » 10年前

お世話になっております。

2次元配列のサイズを可変にするためにmallocとreallocで、各行の要素数が違う配列を使ったプログラムを作成していたのですが、
どこかでおかしな操作をしているらしく動作が非常に不安定になっていて、
原因調べてみたところとreallocが返却アドレスを変更した時が原因だと推測できました。(そのあと再度reallocしようとすると止まる)
以下のコードに示すようなデータの管理方法を行っています。(これだけだと普通に動きますが、危険なコーディングをしてるかもしれません)

コード:

#define LAYER_MAX 6
#define ARRAY_MAX_DEF 80
typedef struct {
    short a0;
    char a1;
    char a2;
}TEST0_t;
typedef struct{
    TEST0_t b0[LAYER_MAX];
    short b1;
    short b2;
} TEST1_t;
int *g_cellsize;
TEST1_t **g_cells;// これにデータを入れる
void InitTEST1_t(TEST1_t*fcell_){// test1_t型オブジェクトを初期化する関数
    for (int rp_i=0; rp_i<LAYER_MAX; rp_i++) {
        fcell_->b0[rp_i].a0=0;
        fcell_->b0[rp_i].a1=0;
        fcell_->b0[rp_i].a2=0;
    }
    fcell_->b1=0;
    fcell_->b2=0;
}
void PointerRealloc(TEST1_t*fcell_,// サイズを変更する配列
                    int nowsize,// 現在の配列サイズ
                    short tosize// 拡張する配列サイズ
                    ){// TEST1_t 型の配列サイズを変更する関数
    // (この関数に原因がある?)
    // fcell_の元々の値を保持
    TEST1_t *pre_cell_;
    pre_cell_= (TEST1_t*) malloc(sizeof(TEST1_t)*nowsize);
    for (int rp_i=0; rp_i<nowsize; rp_i++) {
        pre_cell_[rp_i]=fcell_[rp_i];
    }
    // fcell_のサイズの変更
    fcell_=(TEST1_t*)realloc(fcell_,sizeof(TEST1_t)*tosize);
    // fcell_の初期化
    for (int rp_i=0; rp_i<tosize; rp_i++) {
        InitTEST1_t(&fcell_[rp_i]);
    }
    // fcell_に値を再代入
    for (int rp_i=0; rp_i<tosize&&rp_i<nowsize; rp_i++) {
        fcell_[rp_i]=pre_cell_[rp_i];
    }

}
void appmain(){
    printf("start\n");
    int tosize=140;// 列配列サイズ
    // 確保
    g_cells = (TEST1_t**)malloc(sizeof(TEST1_t*)*ARRAY_MAX_DEF);// 行配列を動的確保
    g_cellsize = (int*)malloc(sizeof(int)*ARRAY_MAX_DEF);
    for (int sti=0; sti<ARRAY_MAX_DEF; sti++) {
        g_cellsize[sti]=tosize;
        g_cells[sti]= (TEST1_t*) malloc(sizeof(TEST1_t)*g_cellsize[sti]);// 列配列を動的確保
        for (int rp_i=0; rp_i<g_cellsize[sti]; rp_i++) {
            InitTEST1_t(&g_cells[sti][rp_i]);// 初期化
        }
    }
    // 配列サイズの変更テスト
    tosize=200;// 配列サイズを140から200に変更する
    for (int sti=0; sti<ARRAY_MAX_DEF; sti++) {
        PointerRealloc(g_cells[sti],g_cellsize[sti],tosize);
        g_cellsize[sti]=tosize;
        for (int i=0; i<tosize; i++) {
            g_cells[sti][i].b1=sti;
        }
        printf("%d\n",g_cells[sti][tosize-1].b1);
    }
}
もしかすると根本的な原因は別にあるのかもしれないのですが、このまま動的確保を多用する方向性で作るのはあまりにも不安なので質問させていただきました。
具体的には、
バグの原因、またはもしこのデータ構造を実現するために別の手段があればアドバイスを頂ければ幸いです。
宜しくお願いします。

Aozora0630
記事: 85
登録日時: 10年前
住所: 日本
連絡を取る:

Re: サイズが可変なデータ構造の実現方法について

#2

投稿記事 by Aozora0630 » 10年前

とても言いにくいのですが、動的確保を無闇矢鱈と使うと、メモリがぐちゃぐちゃになって変に容量が圧迫されてしまいます。

ですので、vectorを使うことをオススメします。

それについては自分で調べてください。

普通の静的配列なら普通の配列で問題ありません。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: サイズが可変なデータ構造の実現方法について

#3

投稿記事 by usao » 10年前

>reallocが返却アドレスを変更した時

PointerRealloc()内のrealloc()が引数と異なる値を返してきても,
そのことがg_cells[]に反映されていないのではないでしょうか.

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: サイズが可変なデータ構造の実現方法について

#4

投稿記事 by みけCAT » 10年前

Aozora0630 さんが書きました:ですので、vectorを使うことをオススメします。
vectorはC言語の標準ライブラリにはありません。
このコードは構造体がtypedefされているからC言語なのか、それとも関数宣言の引数表記が省略されているところがあるからC++なのか、判断が難しいですね。
//のコメント、forの中での変数宣言、ブロックの途中での変数宣言はC言語(C99)でも使えます。
ちなみにcodeタグには言語が書かれていません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

bonbo

Re: サイズが可変なデータ構造の実現方法について

#5

投稿記事 by bonbo » 10年前

ありがとうございます。
返信が遅れ申し訳ありません。
>usaoさん
ありがとうございます。
>PointerRealloc()内のrealloc()が引数と異なる値を返してきても,
>そのことがg_cells[]に反映されていないのではないでしょうか.
これが大きな原因の一つだったみたいです。関数の前後でポインタの値が変更されないことに気がつきました。
以下のように変更したところアドレスが変更されました。

コード:

void PointerRealloc(TEST1_t**fcell_,// サイズを変更する配列
                    int nowsize,// 現在の配列サイズ
                    short tosize// 拡張する配列サイズ
){// TEST1_t 型の配列サイズを変更する関数
    // (この関数に原因がある?)
    // fcell_の元々の値を保持
    TEST1_t *pre_cell_;
    pre_cell_= (TEST1_t*) malloc(sizeof(TEST1_t)*nowsize);
    for (int rp_i=0; rp_i<nowsize; rp_i++) {
        pre_cell_[rp_i]=(*fcell_)[rp_i];
    }
    // fcell_のサイズの変更
    *fcell_=(TEST1_t*)realloc(*fcell_,sizeof(TEST1_t)*tosize);
    // fcell_の初期化
    for (int rp_i=0; rp_i<tosize; rp_i++) {
        InitTEST1_t(&(*fcell_)[rp_i]);
    }
    // fcell_に値を再代入
    for (int rp_i=0; rp_i<tosize&&rp_i<nowsize; rp_i++) {
        (*fcell_)[rp_i]=pre_cell_[rp_i];
    }
}
何も考えずに参照渡しと値渡しをまぜこぜにして関数にしていましたが、関数の中でアドレスが変更される場合は注意が必要なのですね。
>みけCATさん
ありがとうございます。
>このコードは構造体がtypedefされているからC言語なのか、それとも関数宣言の引数表記が省略されているところがあるから>C++なのか、判断が難しいですね。
すみません。
ろくにcを学ばずcppばかり使っているためどれがcppでどれがcなのか未だに理解していないようです。
>forの中での変数宣言、ブロックの途中での変数宣言はC言語(C99)でも使えます。
for中での変数宣言はcの方では使えないと思っていました。
なるべくcに合わせて動くようにしようと思います。
>Aozora0630さん
ありがとうございます。
やはりこの方法だとヒープがガリガリになりそうなのはしょうがないのですね。
>vectorを使うことをオススメします。
調べてみると配列サイズを気にせずコードがかけるとても便利な方法のようですね。知りませんでした。
xcodeでもちょっと設定をいじればvectorが使えるようなので別途調べてみようと思います。
(環境を記載していませんでした。すみません。上記コードはxcodeのiosシミュレータで動作させています。)

また、リスト構造を使う事でも解決の糸口が見えそうです。
まだ解決という訳ではないのですが、とりあえずお教えいただいた事をいろいろ試してみます。
皆様今回はありがとうございました。

bonbo

Re: サイズが可変なデータ構造の実現方法について

#6

投稿記事 by bonbo » 10年前

>関数の前後でポインタの値が変更されない
ポインタの値ではなくアドレスの間違いです。
その他
&(*fcell_)[anynumber]
と書くべきところを
fcell_[anynumber]
にしたり
いろいろ間違っていたようです
修正を加えたところ際立ったバグは全てなくなりました。

閉鎖

“C言語何でも質問掲示板” へ戻る