以下のように定義した、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を探す
おすすめ情報
- ・「みんな教えて! 選手権!!」開催のお知らせ
- ・漫画をレンタルでお得に読める!
- ・「黒歴史」教えて下さい
- ・2024年においていきたいもの
- ・我が家のお雑煮スタイル、教えて下さい
- ・店員も客も斜め上を行くデパートの福袋
- ・食べられるかと思ったけど…ダメでした
- ・【大喜利】【投稿~12/28】こんなおせち料理は嫌だ
- ・前回の年越しの瞬間、何してた?
- ・【お題】マッチョ習字
- ・モテ期を経験した方いらっしゃいますか?
- ・一番最初にネットにつないだのはいつ?
- ・好きな人を振り向かせるためにしたこと
- ・【選手権お題その2】この漫画の2コマ目を考えてください
- ・2024年に成し遂げたこと
- ・3分あったら何をしますか?
- ・何歳が一番楽しかった?
- ・治せない「クセ」を教えてください
- ・【大喜利】【投稿~12/17】 ありそうだけど絶対に無いことわざ
- ・【選手権お題その1】これってもしかして自分だけかもしれないな…と思うあるあるを教えてください
- ・集合写真、どこに映る?
- ・自分の通っていた小学校のあるある
- ・フォントについて教えてください!
- ・これが怖いの自分だけ?というものありますか?
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・10代と話して驚いたこと
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
mallocで確保するメモリの領域...
-
c言語のポインタへの文字列入力...
-
allocってなんですか?
-
スタック破壊の上手な見つけ方...
-
stringの最大サイズ
-
newしないオブジェクトについて
-
c言語のメモリの確保について
-
malloc()関数内でセングメント...
-
メモリ解放について
-
gcnew arrayによるメモリ確保に...
-
LPTSTR型の変数に文字を格納
-
関数から配列を返すには?
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
LPSTR型の初期化について
-
C# Listを使わずに2次元配列の...
-
System.IO.Directory.GetFiles...
-
配列の要素数に変数を入れたい...
-
VB.NETでファイル名順にファイ...
-
「#undef」と「#define」の使い...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
c言語のポインタへの文字列入力...
-
allocってなんですか?
-
newしないオブジェクトについて
-
スタック破壊の上手な見つけ方...
-
C++で、メンバもヒープに確保さ...
-
プログラムが途中で強制終了し...
-
void*型のデータサイズ
-
構造体でchar name[]と*nameの...
-
C++のnewで確保したメモリーの...
-
stringの最大サイズ
-
DLLのマルチスレッドの動作につ...
-
malloc呼び出し時のセグメンテ...
-
行列内の行の交換,列の交換を...
-
画像を読み込む配列の確保。
-
new と malloc によるメモリの...
-
配列の添え字の最大数とは?
-
MSDNがgethostbynameではなくge...
-
Accessで、メモリを開放するタ...
-
ポインタのポインタの初期化法
-
ヒープメモリの解放について
おすすめ情報