以下のように定義した、2重に入れ子になった構造体があります。
これを、mallocを使ってエリアを確保した後、初期化しています。
例では、各構造体の項目数が少ないのですが、
項目が増えた場合、下記のような初期化方法だと面倒だと思います。
もっとよい方法があるのだろうと思っているのですが・・・。
下記は私の試行錯誤結果なので、「普通はこうやるんじゃないの?」という方法などがあればご教授願います。
/* -- 構造体定義 -- */
typedef struct {
char data[16] ;
}D_TAG ;
typedef struct {
char name[16];
D_TAG *d ;
}F_TAG ;
typedef struct {
char name[16] ;
F_TAG *f ;
}T_TAG ;
/* -- 変数定義 -- */
T_TAG *t ;
/* -- エリア確保 -- */
t=(T_TAG*)malloc(sizeof(T_TAG)*10);
for(i=0;i<10;i++) {
t[i].f=(F_TAG*)malloc(sizeof(F_TAG)*10);
for(j=0;j<10;j++) {
t[i].f[j].d = (D_TAG*)malloc(sizeof(D_TAG)*10);
}
}
/* -- 初期化 -- */
for(i=0;i<10;i++) {
memset(t[i].name , 0x00, sizeof(t[i].name));
for(j=0;j<10;j++) {
memset( t[i].f[j].d, 0x00, sizeof(D_TAG)*10);
memset( t[i].f[j].name , 0x00, sizeof(t[i].f[j].name));
}
}
よろしくお願いいたします。
No.1ベストアンサー
- 回答日時:
初期化値が0なら、mallocではなくcallocでメモリ確保
されてはどうでしょう?自動で0に初期化してくれますよ。
参考URL:http://www.bohyoh.com/CandCPP/C/Library/calloc.h …
早速のご回答ありがとうございます。
「calloc」は使ったことが無かったです。
早速使ってみました。ありがとうございます!
ちなみに、もし、スペースなど0以外で全て初期化したいような場合は、地道にやることになるのでしょうか?
No.5
- 回答日時:
なんとなく、sunasaka3さんのやりたい事が
見えてきたような思い込みを当方が勝手にしたので
参考程度に。
T_TAG/F_TAG/D_TAGの各内部が動的で必要数も変わるんですな。
高速化の手助けには成らないですけど、まとめ方の例として
線形リストを用いて観てはいかがでしょうか。
例(あえて片方向線形リストっ)
typedef struct _tag_N_TAG { // データ格納構造体
_tag_N_TAG *pnext; // 自分の次に来るN_TAG構造体へのポインタ(NULL時、終末)
_tag_N_TAG *pdata; // 更に下に格納してるデータ(NULL時、下にデータ無し)
char *name; // 格納してる名前
} N_TAG;
// N_TAGを確保し初期化、線形リストに追加する処理
// **pTOP: この横階層のトップ(絶対!でないとリストが破綻する(横着してるから…
// name : 名称
N_TAG* CreateN_TAG(N_TAG **pTOP, char *name)
{
N_TAG *p;
if( !(p = (N_TAG*)malloc(sizeof(N_TAG))) ) return(NULL); // メモリ不足
if( !(p->name = (char*)malloc(strlen(name)+1)) ) { // 名前の領域を確保
free(p); return(NULL); // メモリ不足
}
p->pnext = *pTop; // 作成したpの次を引数である先頭にし
*pTop = p; // 作成したpが先頭になる
p->data = NULL; // 作成したばかりなので下にデータは無し
strcpy(p->name, name); // 名前をコピーする
return(p); // 作成したN_TAGであるpを返す
}
使用例
void main(void)
{
N_TAG *root = NULL;
N_TAG *p;
p = CreateN_TAG(&root, "a"); // root階層に付け足す
CreateN_TAG(&root, "b"); // 付け足す(リストをたどれば
CreateN_TAG(&root, "c"); // 足す 作成したN_TAGを探せるのでポインタ捨てる)
CreateN_TAG(&p->pdata, "m"); // aの下方階層に付け足す
CreateN_TAG(&p->pdata, "n"); // 付け足す
p = CreateN_TAG(&p->pdata, "o"); // 足す
CreateN_TAG(&p->pdata, "q"); // oの下方・・・ry
// cを探す(nとか下の階層だと、関数作らないとダメなので…横着…(;;
p = root;
while(p) { if(!(strcmp(p->name, "c"))) break; p = p->pnext; } // こないなカンジ
if(p) { /* pは、"c"のN_TAGポインタ */ }
// 後始末… 書かなきゃダメか…
ReleaseN_TAG(root); // 下記
}
// pR:階層のトップで無いとダメ。
void ReleaseN_TAG(N_TAG *pR)
{
N_TAG *p, *q;
p = pR;
while(p) {
if(p->pdata) ReleaseN_TAG(p->pdata); // 下方階層があるなら、再帰する
q = p; // 開放用
p = p->pnext; // 次を取り出す
free(q->name); // 名称領域開放
free(q); // 自身開放
}
}
// -----------------------------------------------------------------------------
イメージ的には
↓pnextね
root == c -> b -> a -> NULL (横階層 pnext->pnext…)
| ←pdataね(下階層 pdata)
o -> n -> m -> NULL
|
q -> NULL
てな、カンジの構造が作れます。
CreateN_TAG関数は、常に先頭に足していく様に作ったので
上記のようなデータの並びになります。
データの取り出しは、rootからpnextやpdataをたどっていきます。(開放もね)
途中のデータを削除・追加・ソートとか考えてない作りです。(この例はね)
理解できれば、双方向な線形リスト化や、
削除・追加関数・ソート関数の作成も容易になります。
PS.判りづらいかも…
++余計な、おせっかいかも
+++すみません…(TT
++++コードにロジックエラーが在るかも…
線形リストですか。
情報処理とかの勉強で出てきますが、実際にやってみたことは無かったです。
たしかに、やりたいことを図示するとリストになるのですよね。
やってみたいと思います。
ありがとうございました!
No.4
- 回答日時:
なんとなく構造が複雑でプログラミングが進んでいくとメモリリークを起こしてしまいそうですね。
入れ子を使わずにtypedef struct {
char data[16];
char name1[16];
char name2[16];
}T_TAG ;
のようにしてしまってはダメなのですか?後々の処理でdataやnameのポインタが移動するからというのでしたら
typedef struct {
char* data;
char* name1;
char* name2;
}T_TAG ;
のようにしてポインタ扱いにしてしまってもいいと思いますし...
この手の入れ子を使わなくてはいけないというような条件がないのでしたら見やすいコードにするとメモリリークやバグの可能性も減らせていいかと思います。
アドバイスありがとうございます。
自分としてもあまり複雑にはしたくなかったのですが、方法が思い浮かばず・・・。
今回、このような構造にしたのは、それぞれ入れ子になっている構造体のサイズが、nameの配列要素ごとに違っている為です。
なので、実際は、質問のように、まとめてmalloc後、纏めて初期化ということはしていません。
イメージとしては、以下のようにDBっぽい扱いです。
・T_TAG→テーブル名を格納
・F_TAG→各テーブルのフィールド名を格納
・D_TAG→各フィールドのデータを格納
テーブル毎にフィールド数が異なり、各テーブルでデータ数が異なるものを纏めて扱うことが目的です。
テーブル名、フィールド名にあたるものはも外部ファイルから取得して動的に作成しなければならないのです。
なんか、無理やり感が在るのですが・・・。
いい方法があればアドバイスいただければ幸いです。
よろしくお願いいたします。
No.3
- 回答日時:
>ちなみに、質問では省いたのですが、freeはmallocの逆順に入れ子の内側からやっていく、ということになるのでしょうか?
>(今は、そうしています。)
補足回答です。
ならべくなら、入れ子の内側からの方が良いです。(確保した順番の逆の意)
開放メモリの分断化を防ぐ観点からはですが…
しかしながら、メモリアロケイターが確保の逆順に開放したからといって、
必ずしも、最適にメモリを管理してくれてる保証はないので、何とも言えません。
(確保時にメモリセル自体が分断化された状況ってのありうるし…)
今回、質問した例だと内側から開放していかないといけないですね。
(外側からだと、内側で確保したメモリポインタが参照できなくなってしまうから…(^-^)
念の為(しつこい?)外側から開放した場合…
F_TAG型のt[0].f[1]を開放してしまったら、
D_TAG型のt[0].f[1].dに格納されていたメモリ確保ポインタが参照・開放出来なくなってしまう。
※開放する前に他で取って置けば別ですが…
具体例:
char *a = (char*)malloc(10);
char *b = (char*)malloc(10);
free(a); free(b);
ってのは、アリです。(釈迦に説法っぽぃなぁ…)
と、省かれた質問のアドバイスでした。
更に補足~
#2で書いたC++部
delete[] t;
ってのは、
delete時にT_TAGのデスクトラクター~T_TAG()が呼ばれ、
~T_TAG()の中から、~F_TAG()が呼ばれ、
~F_TAG()の中から、~D_TAG()が呼ばれます。
そして、戻っていく訳なので…
つまり、最内側から開放されていく訳です。
No.2
- 回答日時:
#1さんのcalloc使用に同意しますね。
各構造体毎に初期化マクロを用意するというのも、手です。
#define m_clrD_TAG(prm) memset(prm->name,0,sizeof(prm->data));
#define m_clrF_TAG(prm) { memset(prm->name,0,sizeof(prm->name)); prm->d = NULL; }
#define m_clrT_TAG(prm) { memset(prm->name,0,sizeof(prm->name)); prm->f = NULL; }
m_clrT_TAG(&t[0])
とか。
まあ、一部クリアーとか発生するような状況でもない限りは…(ポインタもクリアーしてるから、リークとかに注意)
以下は、とっても暇な時にでも…
C++なら構造体にコンストラクターつけて、
クリアーをする手があります。
へたっぴな、C++で申し訳ないですが
あくまで御参考に~。(Cオンリーなら、全然参考にならないデス)
定義
class D_TAG {
public: char data[16];
D_TAG() { memset(data, 0, 16); } // newで作成する時に自動的に呼ばれる
~D_TAG() {}
};
class F_TAG {
public: char name[16];
D_TAG* d;
F_TAG() { memset(name, 0, 16); d = NULL; }
~F_TAG() { if(d) delete[] d; } // delete時に呼ばれる。dが確保されているなら開放する
};
class T_TAG {
public: char name[16];
F_TAG* f;
T_TAG() { memset(name, 0, 16); f = NULL; }
~T_TAG() { if(f) delete[] f; }
};
初期化使用例
T_TAG* t = new T_TAG[10]; // newで作成時に初期化される
for(int i=0;i<10;i++) {
t[i].f = new F_TAG[10];
for(int j=0;j<10;j++) {
t[i].f[j].d = new D_TAG[10];
}
}
開放例
delete[] t; // コレだけで全部まとめて開放してくれる(tの中のfもfの中のdもね)
delete[] t[1].f[5].d;
t[1].f[5].d = NULL; // コレは、必ず!
※[T_TAGのf][F_TAGのd]は、各個に開放したら必ずNULLを入れないといけない。
PS.
C++へのステップアップにと勝手に私が思い込んでみました。
ご迷惑でなければ良いのですが…
「C言語しか環境的につかえんのだよ、だから質問してるんだよ!」
って、実は!?だったら、すみません。
組み込みexeGCCでヒープが湯水のようにつかえない、とかだったり…
ご回答ありがとうございます。
今回は、「Cオンリー」なのです・・・。
C++の方は今後の参考とさせていただきます。
callocを使ってやってみたのですが、遅くなってしまいちょっと微妙な状況です。
もともと3秒くらいかかって「遅い」といわれていたのですが、callocだと5秒かかってしまい・・・。
ですので、マクロを使用する方法で試してみます!
ちなみに、質問では省いたのですが、freeはmallocの逆順に入れ子の内側からやっていく、ということになるのでしょうか?
(今は、そうしています。)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語初心者 構造体 課題について 1 2023/03/10 19:30
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# プログラムが書けません。 4 2023/01/22 22:57
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- Visual Basic(VBA) ①ExcelVBAでカレンダーを作り、別のユザーフォームで日付を入力したいのですがエラーになります。 1 2023/02/17 18:39
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- その他(プログラミング・Web制作) pythonのWebスクレイピングでfind_allだとurlがNoneに 4 2022/04/17 18:21
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/22】このサンタクロースは偽物だと気付いた理由とは?
- ・お風呂の温度、何℃にしてますか?
- ・とっておきの「まかない飯」を教えて下さい!
- ・2024年のうちにやっておきたいこと、ここで宣言しませんか?
- ・いけず言葉しりとり
- ・土曜の昼、学校帰りの昼メシの思い出
- ・忘れられない激○○料理
- ・あなたにとってのゴールデンタイムはいつですか?
- ・とっておきの「夜食」教えて下さい
- ・これまでで一番「情けなかったとき」はいつですか?
- ・プリン+醤油=ウニみたいな組み合わせメニューを教えて!
- ・タイムマシーンがあったら、過去と未来どちらに行く?
- ・遅刻の「言い訳」選手権
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ヒープメモリの解放について
-
スタック破壊の上手な見つけ方...
-
malloc呼び出し時のセグメンテ...
-
allocってなんですか?
-
グローバル変数のサイズ
-
ピクチャボックス内の線を選択...
-
ポインタのポインタの初期化法
-
stringの最大サイズ
-
mallocで確保するメモリの領域...
-
構造体を使ったファイルの読み込み
-
C++のnewで確保したメモリーの...
-
newしないオブジェクトについて
-
c言語のポインタへの文字列入力...
-
メモリ管理
-
プログラムが途中で強制終了し...
-
C言語 配列の長さの上限
-
関数から配列を返すには?
-
Integer変数をカラにしたいので...
-
init関数の意味
-
セグメントエラー
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
c言語のポインタへの文字列入力...
-
newしないオブジェクトについて
-
allocってなんですか?
-
malloc呼び出し時のセグメンテ...
-
ヒープメモリの解放について
-
配列の添え字の最大数とは?
-
C++で、メンバもヒープに確保さ...
-
ビットをローテートするプログ...
-
プログラムが途中で強制終了し...
-
構造体でchar name[]と*nameの...
-
stringの最大サイズ
-
mallocで確保するメモリの領域...
-
C言語 mallocとfreeについて
-
void*型のデータサイズ
-
大容量の静的な確保の限界値
-
DLLのマルチスレッドの動作につ...
-
スタック破壊の上手な見つけ方...
-
C++のnewで確保したメモリーの...
-
行列内の行の交換,列の交換を...
-
文字列ポインタとgets関数の関...
おすすめ情報