質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

751閲覧

構造体を使ったアドレス帳のデータ追加関数がうまくいかない

kotarou9

総合スコア3

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2023/04/28 08:57

前提

C言語の勉強で構造体を使ったアドレス帳のようなプログラムを作成しています。
問題ではデータ1件ごとにmallocを使用してメモリの動的確保を行わないといけないため
add_data関数に入る前に一度mallocを使用していますadd_data関数自体は問題なくうごいていますがデータ一覧を表示すると直近で追加したデータ以外おかしな表示がされてしまいます
なぜこのようになるのかわからなく相談させていただきました。
mallocであたらしいメモリの割り当てを取っているつもりでしたがとれていないのでしょうか?
それとも、そもそもの所これだと追加できていないのでしょうか?
少しヒントをいただければ幸いです

発生している問題・エラーメッセージ

仮に3件追加した場合 No.1 名前: 住所: 生年月日: 電話番号: No.2 名前: 住所: 生年月日: 電話番号: No.3 名前:太郎 住所:日本 生年月日:2023/04/28 電話番号:090-1234-5678 3件目は保存されていますが1.2件目は例のため空白になっていますが おかしな文字列が入っていたりします

該当のソースコード

C言語

1ソースコード 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6typedef struct { 7 char name[22]; 8 char address[130]; 9 char birth[12]; 10 char tell[18]; 11}kojin; 12 13void show_data(kojin *data, int size); 14//void search_data(struct kojin *data); 15void add_data(kojin *data, int size); 16//void change_data(); 17//void delete_data(); 18 19 20int main(void) 21{ 22 kojin *data = NULL; 23 int size = 0; 24 int moji; 25 int i; 26 27 while(1) 28 { 29 printf("-----メニュー-----\n"); 30 printf("1. データ一覧\n"); 31 printf("2. データ一件\n"); 32 printf("3. データの追加\n"); 33 printf("4. データの変更\n"); 34 printf("5. データの削除\n"); 35 printf("6. 終了\n"); 36 printf("\n"); 37 38 moji = getchar(); 39 while (getchar() != '\n'); 40 41 switch (moji) 42 { 43 case '1': 44 printf("1. データ一覧\n"); 45 show_data(data, size); 46 break; 47 48 case '2': 49 printf("2. データ一件閲覧\n"); 50 break; 51 52 case '3': 53 printf("3. データ追加\n"); 54 data = (kojin *)malloc((size + 1) * sizeof(kojin)); 55 if (data == NULL) 56 { 57 printf("メモリの確保に失敗しました。\n"); 58 } 59 add_data(data, size); 60 size++; 61 break; 62 63 case '4': 64 printf("4. データ変更\n"); 65 break; 66 67 case '5': 68 printf("5. データ削除\n"); 69 break; 70 71 case '6': 72 printf("6. 終了します\n"); 73 free(data); 74 exit(0); 75 76 default: 77 printf("入力エラー\n"); 78 } 79 } 80} 81 82void add_data(kojin *data, int size) 83{ 84 int input; 85 int i, j; 86 int flag = 0; 87 88 while(1) 89 { 90 printf("名前の入力\n"); 91 j = 0; 92 while ((input = getchar()) != '\n') 93 { 94 data[size].name[j] = input; 95 j++; 96 } 97 if (j > 0) 98 { 99 data[size].name[j] = '\0'; 100 break; 101 } 102 printf("名前は必須の入力欄になります。\n"); 103 } 104 105 printf("住所の入力\n"); 106 j = 0; 107 while ((input = getchar()) != '\n') 108 { 109 data[size].address[j] = input; 110 j++; 111 } 112 data[size].address[j] = '\0'; 113 114 while (1) 115 { 116 printf("生年月日入力\n"); 117 j = 0; 118 flag = 0; 119 while ((input = getchar()) != '\n') 120 { 121 if (input >= '0' && input <= '9' || input == '/') 122 { 123 data[size].birth[j++] = input; 124 } 125 else 126 { 127 flag = 2; 128 } 129 } 130 if (flag == 0) 131 { 132 data[size].birth[j] = '\0'; 133 break; 134 } 135 else 136 { 137 printf("入力エラー\n"); 138 } 139 } 140 141 while (1) 142 { 143 printf("電話番号を入力\n"); 144 j = 0; 145 flag = 0; 146 while ((input = getchar()) != '\n') 147 { 148 if (input >= '0' && input <= '9' || input == '-') 149 { 150 data[size].tell[j++] = input; 151 } 152 else 153 { 154 flag = 2; 155 } 156 } 157 if (flag == 0) 158 { 159 data[size].tell[j] = '\0'; 160 break; 161 } 162 else 163 { 164 printf("入力エラー\n"); 165 } 166 } 167 printf("-----入力内容-----\n"); 168 printf("名前:%s\n", data[size].name); 169 printf("住所:%s\n", data[size].address); 170 printf("生年月日:%s\n", data[size].birth); 171 printf("電話番号:%s\n", data[size].tell); 172 printf("\n"); 173} 174 175void show_data(kojin *data, int size) 176{ 177 if (size == 0) 178 { 179 printf("データはありません。\n"); 180 return; 181 } 182 183 printf("-----データ一覧-----\n"); 184 for (int i = 0; i < size; i++) 185 { 186 printf("No.%d\n", i+1); 187 printf("名前:%s\n", data[i].name); 188 printf("住所:%s\n", data[i].address); 189 printf("生年月日:%s\n", data[i].birth); 190 printf("電話番号:%s\n", data[i].tell); 191 printf("\n"); 192 } 193} 194 195 196

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

malloc は指定したサイズのメモリを(未使用のメモリから)確保します。
2件のデータが入った領域と3件のデータが入った領域は関係ありません。2件のデータが入った領域を示す変数に新たな領域のアドレスを入れていることも関係ありません。
プログラマは「2件のデータが入った領域を大きくした」イメージでも、コンピュータからみれば2件のデータの入った領域の他に3件分の領域を確保しただけ(そして2件のデータの入っていた領域のポインタに上書きしているだけ)です。
2件のデータを引き継いだ形にしたいのなら、2件のデータの領域は保持しておきコピーする等する必要があります。
もしくは malloc では無く realloc の使用を検討してください。

データをコピーする例

c

1 case '3': 2 printf("3. データ追加\n"); 3 kojin *newData = malloc((size + 1) * sizeof(kojin)); //新しい領域へのアドレスを一旦別の変数に保存 4 if(newData == NULL) { 5 printf("メモリの確保に失敗しました。\n"); 6 break; //失敗したら後続の処理をしては行けないはず 7 } 8 if(data != NULL) { //古い領域があるならデータが入っているはず 9 memcpy(newData, data, size * sizeof(kojin)); //古い領域から新しい領域へデータをコピー 10 free(data); //古い領域を解放 11 } 12 data = newData; //新しい領域に書き換え 13 add_data(data, size); 14 size++; 15 break;

realloc の例

c

1 case '3': 2 printf("3. データ追加\n"); 3 kojin *reData = realloc(data, (size + 1) * sizeof(kojin)); //NULL の可能性があるため一旦別の変数に保存 4 if(reData == NULL) { 5 printf("メモリの確保に失敗しました。\n"); 6 break; //失敗したら後続の処理をしては行けないはず 7 } 8 data = reData; //書き換え (新しい領域ならデータは realloc によってコピーされている) 9 add_data(data, size); 10 size++; 11 break;

投稿2023/04/28 09:26

編集2023/04/28 18:39
jimbe

総合スコア12545

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kotarou9

2023/04/28 13:46

54行目のdata = (kojin *)malloc((size + 1) * sizeof(kojin));でsizeを増やすことにより データ1を入れる容器を1つデータ2を入れる容器を二つ用意しているつもりでしたが容器が大きくなっただけで容器の個数は増えてないといった感じでしょうか?
jimbe

2023/04/29 16:56 編集

>データ1を入れる容器を1つデータ2を入れる容器を二つ どういう動きを説明されているのか分かりませんが。 提示されているプログラムは data が指す構造体配列を用い、その大きさが size に入っていることにしています。 最初の実行時、つまり size=0 でデータの追加を選択すると、 構造体一つ分のメモリが確保されて data に設定され、 add_data 関数によってその一つ分のメモリに値が設定されます。この時の data の指すメモリが仮に 0x1000 だったとすると kojin 構造体の大きさは 182 バイトですので 0x1000 ~ 0x10b5 が data の指す領域となります。 さて、続いて二回目の追加を実行すると構造体二つ分のメモリが確保されて data に設定されます。仮に 0x2000 だったとしたら 0x2000 ~ 0x216c が新しい領域となります。この時、最初のメモリのアドレス(0x1000)は上書きされて分からなくなります(malloc したメモリは free しなければならないのに、それも出来なくなります) 。 add_data 関数によって構造体二つ分のメモリのうち二つ目(0x20b6~0x216c)に値が設定されます。 ここ迄で気づかれるかも知れませんが、最初の追加で add_data が値を設定したのは 0x1000 からのメモリであって、それは既に(二回目の操作によって)行方が分からなくなっています。一方二回目の入力で値を設定されたのは 0x20b6 からで、 一つ目にあたる 0x2000 ~ 0x20b5 には何もしていません(クリアもしていないのでゴミだらけかもしれません)。 同じことが三回目でも起こります。構造体三つ分の領域を確保して data に設定すると二回目で確保し二つ目(だけ)に値を設定をした領域は行方不明となり、新たに確保した領域の三つ目に値を設定しても一つ目~二つ目には何もせずゴミのままです。 その状態で data が示すアドレスから三つ分を表示したら、二つ分のゴミと三つ目の値が表示されるでしょう。
kotarou9

2023/04/29 14:04

jimbeさんありがとうございます! 解説もわかりやすく勘違いしている部分も分かりすっきりしました! 再度コードの書き直しやポインタの使い方も含め勉強していきます
guest

0

jimbeさんが言われていることを図示すると以下のようになります。
イメージ説明
イメージ説明
1回目のmallocの時は、問題ありません。
2回目のmallocの時、1回目でmallocしたメモリを参照するポインターがなくなります。
そして、2人目のデータは、2回目にmallocしたメモリの先頭に書き込まれます。
3回目のmallocの時、1,2回目でmallocしたメモリを参照するポインターがなくなります。
そして、3人目のデータは、3回目にmallocしたメモリの先頭に書き込まれます。

あなたが、すべきことは、以下のような状態にすることです。
イメージ説明

その為には、毎回
1.前回mallocしたメモリのデータを、今回mallocしたデータにコピーする。
2.前回mallocしたメモリを解放する。
3.今回分のデータを書き込むのは、mallocしたメモリの先頭ではなく、最後に書き込むようにする。
という作業が必要になります。

投稿2023/04/28 23:57

編集2023/04/29 08:04
tatsu99

総合スコア5424

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tatsu99

2023/04/29 08:05

前回の投稿に誤りがありましたので、修正しました。
kotarou9

2023/04/29 14:05

図解までありがとうございます 形を目で追えるためわかりやすく非常に助かります!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問