- ベストアンサー
文字列配列をメンバにもつ構造体の,メモリ動的確保
下に同様の質問があるのですが,やはり理解できません. 文字列配列をメンバにもつ構造体の,メモリを動的に確保をしたいのですが,うまくいきません. 具体的には以下のようです. 正しくはどのようにすればよいでしょうか.よろしくお願いします. typedef struct { char **boy; char **girl; } Name_class; int init_name_class(Name_class name_class, int n_boy, int n_girl) { int i; name_class.boy = (char**) malloc( n_boy * sizeof(char**) ); for(i=0; i<n_boy; i++) name_class.boy[i] = (char*) malloc( 32 * sizeof(char*) ); name_class.girl = (char**) malloc( n_girl * sizeof(char**) ); for(i=0; i<n_girl; i++) name_class.girl[i] = (char*) malloc( 32 * sizeof(char*) ); } main(int argc, char *argv[]) { int i, j, n_boy=3, n_girl=2; Name_class name_class; init_name_class(name_class, n_boy, n_girl); name_class.boy[0] = "yukio"; name_class.boy[1] = "hideaki"; name_class.boy[2] = "mitsuru"; name_class.girl[0] = "youko"; name_class.girl[1] = "chiharu"; printf("%s, %s, %s, %s, %s\n", name_class.boy[0], name_class.boy[1], name_class.boy[2], name_class.girl[0], name_class.girl[1]); }
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
×name_class.boy = (char**) malloc( n_boy * sizeof(char**) ); ○name_class.boy = (char**) malloc( n_boy * sizeof(char*) ); メンバboyは、char**のサイズの領域をn_boy個ではなく、char*のサイズの領域をn_boy個欲しい筈。 ×name_class.boy[i] = (char*) malloc( 32 * sizeof(char*) ); ○name_class.boy[i] = (char*) malloc( 32 * sizeof(char) ); boy配列の各要素は、char*サイズの領域が32個分ではなく、charサイズの領域が32個分、つまりは32文字分の領域が欲しい筈。 以下、girlメンバについても同様。 更に、 init_name_class(name_class, n_boy, n_girl); で動的にメモリ確保したのに name_class.boy[0] = "yukio"; name_class.boy[1] = "hideaki"; name_class.boy[2] = "mitsuru"; name_class.girl[0] = "youko"; name_class.girl[1] = "chiharu"; とやってしまうと、動的に確保したメモリのポインタは忘れ去られ、スタティックな文字列へのポインタが配列要素に上書き代入されます。 そして、動的に確保したメモリは取り残され、だれも開放すること無く残ります。これをメモリリークと言います。 多分、free関数で開放してないのは「開放しようとするとメモリエラーで例外が発生してアプリが飛ぶ」からでしょうけど、動的に確保したメモリのポインタを書き潰してるので、メモリエラーが出て当然です。 strncpy(name_class.boy[0],"yukio",31); name_class.boy[0][31]='\0'; strncpy(name_class.boy[1],"hideaki",31); name_class.boy[1][31]='\0'; strncpy(name_class.boy[2],"mitsuru",31); name_class.boy[2][31]='\0'; strncpy(name_class.girl[0],"youko",31); name_class.girl[0][31]='\0'; strncpy(name_class.girl[1],"chiharu",31); name_class.girl[1][31]='\0'; のように、(1つに付き32バイト分確保した)メモリに31文字以内で文字列コピーしましょう。 strncpyは、コピー時に指定したバイト数を超えそうになると終端文字の\0を書き込まないので、念の為32文字目に\0を書いておくようにしましょう。 また、mainが終る前に、free関数で確保したメモリを開放しましょう。開放の順番間違いや開放し忘れは、メモリリークの原因になります。 以下の2つのプログラムの違いが判りますか? main() { char *p; p = "test"; printf("%s\n",p); } main() { char *p; p = (char *)malloc(10 * sizeof(char)); strncpy(p,"test",9); p[9]='\0'; printf("%s\n",p); free(p); } 以下の2つのプログラムは正しくありません。判りますか?(実行するとメモリエラーで例外終了します) main() { char *p; p = (char *)malloc(10 * sizeof(char)); p = "test"; printf("%s\n",p); free(p); } main() { char *p; strncpy(p,"test",9); p[9]='\0'; printf("%s\n",p); }
その他の回答 (3)
- a-saitoh
- ベストアンサー率30% (524/1722)
Ano1.のものです。 手元のコンピュータでは取り合えるコンパイルできてセグメンテーションフォルトにもならなかったもので、失礼しました。落ち着いてよく読むと、Ano2,3のかたが指摘されておられるように、mallocに指定するサイズ計算が変ですね。 それと、 name_class.boy[0] = "yukio"; の件ですが。 まず、name_class.boy[0]の中身は 今後上書きで書き換える予定があるので、 "yukio"のバイト数にかかわらず32文字分確保する必要があるのでしょうか?それとも、"yukio"さえ入ればいいのでしょうか? 前者なら、ANo.3 の解答通りです。 後者なら, for(i=0; i<n_girl; i++) name_class.girl[i] = (char*) malloc( 32 * sizeof(char*) ); は、 for(i=0; i<n_girl; i++) name_class.girl[i] = NULL; でよくて、 name_class.boy[0] = "yukio"; は書き換える必要はありません。 なお、このプログラム例のように文字列リテラルなどではなく、ファイル等から読み込んだ文字列 s をセットするのなら、 name_class.boy[0] = strdup(s); とするのが楽ちんです。もちろん、正確には、 if((name_class.boy[0] = strdup(s))==NULL) { fprintf(stderr, "Memory Allocation Failed\n"); exit(1); } みたいな感じで、エラー処理が必要ですが。
お礼
ご回答ありがとうございます. さらに新しい理解を得ることができました. 構造体に格納した名前は,書き換えるかどうかまだ決まっていないのですが,書き換えない場合,ご説明していただいたとおりでよいのだ,ということは全くの未知でありました. また,文字列は,実際にはファイルから読み込むことを想定しております.strdup という関数は知りませんでしたし,エラー処理の仕方も,書いていただいたとおり,そのままありがたく使わせていただきたいと思います. 回答をいただいたみなさんのおかげで,ずいぶん理解のレベルが深まった気になってます. どうも,ありがとうございました.
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
int init_name_class(Name_class name_class, int n_boy, int n_girl) の部分、構造体は参照渡しではなく、値渡しなので、 void init_name_class(Name_class *name_class, int n_boy, int n_girl) とします。 (値を返していないのでvoid で) そのように変更したら メンバーへのアクセスは name_class->boy のようにします 呼び出す側は、 init_name_class(&name_class, n_boy, n_girl); のようにします。 ---------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> typedef struct { char **boy; char **girl; } Name_class; void init_name_class(Name_class *name_class, int n_boy, int n_girl) { int i; name_class->boy = (char**) malloc( n_boy * sizeof(char**) ); for(i=0; i<n_boy; i++) name_class->boy[i] = (char*) malloc( 32 * sizeof(char*) ); name_class->girl = (char**) malloc( n_girl * sizeof(char**) ); for(i=0; i<n_girl; i++) name_class->girl[i] = (char*) malloc( 32 * sizeof(char*) ); } main(int argc, char *argv[]) { int i, j, n_boy=3, n_girl=2; Name_class name_class; init_name_class(&name_class, n_boy, n_girl); name_class.boy[0] = "yukio"; name_class.boy[1] = "hideaki"; name_class.boy[2] = "mitsuru"; name_class.girl[0] = "youko"; name_class.girl[1] = "chiharu"; printf("%s, %s, %s, %s, %s\n", name_class.boy[0], name_class.boy[1], name_class.boy[2], name_class.girl[0], name_class.girl[1]); }
お礼
お礼が遅れてすみません.しばらく考えておりました. ようやく書いていただいたことが分かりました. 構造体の扱いだけでなく,ポインタに関する理解も足りないことがわかりました. もう一度,教科書を読んでしっかり理解したいと思います. ありがとうございました.
- a-saitoh
- ベストアンサー率30% (524/1722)
このプログラムのどこが不満で改良したいのでしょうか? とりあえず、動くだけは動くプログラムですが。
お礼
お返事ありがとうございます. 私の環境が Windows 2000 のcygwin上なのですが,Segmentation fault (core dumped) になってしまいます. 何故でしょうね?
お礼
書いていただいたことをなんとなくですが,理解しました. ポインタとそれがさす実体の関係,スタティックなメモリ領域とその他の(?)メモリ領域に配慮すること.メモリの管理や,文字列を扱う際の '\0' に対する配慮など,もりだくさんの内容に関して,理解するのに十分なご説明とプログラムの例示をいただいたという感想をもちました. 書いていただいたことを教科書にして,しっかり理解したいと思います. 見知らぬ方々の親切なご回答に,たいへん感謝の念をいだいております. ありがとうございました.