下に同様の質問があるのですが,やはり理解できません.
文字列配列をメンバにもつ構造体の,メモリを動的に確保をしたいのですが,うまくいきません.
具体的には以下のようです.
正しくはどのようにすればよいでしょうか.よろしくお願いします.
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]);
}
No.3ベストアンサー
- 回答日時:
×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);
}
書いていただいたことをなんとなくですが,理解しました.
ポインタとそれがさす実体の関係,スタティックなメモリ領域とその他の(?)メモリ領域に配慮すること.メモリの管理や,文字列を扱う際の '\0' に対する配慮など,もりだくさんの内容に関して,理解するのに十分なご説明とプログラムの例示をいただいたという感想をもちました.
書いていただいたことを教科書にして,しっかり理解したいと思います.
見知らぬ方々の親切なご回答に,たいへん感謝の念をいだいております.
ありがとうございました.
No.4
- 回答日時:
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 という関数は知りませんでしたし,エラー処理の仕方も,書いていただいたとおり,そのままありがたく使わせていただきたいと思います.
回答をいただいたみなさんのおかげで,ずいぶん理解のレベルが深まった気になってます.
どうも,ありがとうございました.
No.2
- 回答日時:
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]);
}
お礼が遅れてすみません.しばらく考えておりました.
ようやく書いていただいたことが分かりました.
構造体の扱いだけでなく,ポインタに関する理解も足りないことがわかりました.
もう一度,教科書を読んでしっかり理解したいと思います.
ありがとうございました.
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Application.ScreenUpdating = ...
-
VBAでPDFのコピーとリネームを...
-
コンボボックスのtag情報の取得...
-
エクセルVBA シート名の部分一...
-
ACCESS テキストボックスを隙...
-
実行時エラー 3020の対策
-
phpでボタンを押したときに変数...
-
構造体の各データの表示につい...
-
シェル変数でYYYYMMDDをいれた...
-
JSONで文字列が長い時
-
16進の10進変換について
-
関数「exists」と「defined」の...
-
テーブル内でドロップダウンメ...
-
switch文のエラーについて
-
formで特定のinputを送信しない...
-
Perlで自分自身の関数名を知る...
-
Perlからメール送信で文字化け
-
python3について。
-
<SELECT>タグの折り返し
-
子windowsからsubmit()後にclos...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Application.ScreenUpdating = ...
-
実行時エラー 3020の対策
-
FindFirst を複数条件で検索
-
VBAでPDFのコピーとリネームを...
-
ACCESS テキストボックスを隙...
-
[python] 文字列を変数名として...
-
2つのpythonがあって、一方で...
-
「*:*」って何を意味するのでし...
-
構造体の各データの表示につい...
-
コンボボックスのtag情報の取得...
-
phpでボタンを押したときに変数...
-
UWSC:ポップアップウインドウ...
-
C++ コマンドプロンプトでの入...
-
vbaでxmlからNodeListでデータ...
-
MSXMLでの属性の存在確認法
-
vscode 文字化け
-
この英語はどういう意味ですか?
-
VBA他のブックから値のみ貼付す...
-
[ASP.net Ajax]フレーム分割し...
-
講義でわからないのですが以下...
おすすめ情報