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

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

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

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

Q&A

解決済

2回答

7131閲覧

動的メモリの解放とエラーC6385の解消

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2021/04/22 07:41

前提・実現したいこと

エラーの解消とmalloc,reallocのメモリ解放の確認。

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

エラーはc6385の1つで74行目でおきていて、

'pP0' から無効なデータを読み取っています: 読み取り可能なサイズは 'row*sizeof(KAKEIBO_INFO)' バイトですが、'88' バイトを読み取る可能性があります。

というエラーが出ます。
また、121行目の/**/で囲ってるfree(pP0);で動作が止まります。

該当のソースコード

c

1#define _CRT_SECURE_NO_WARNINGS 2#include<stdio.h> 3#include<string.h> 4#include<stdlib.h> 5//#include"ファイル制御.h" 6 7typedef struct { 8 char hinmoku[40]; 9 int price; 10}KAKEIBO_INFO; 11 12#define STRSIZE 40 13 14int main(void) { 15 16 KAKEIBO_INFO info = { 0,0 }; //家計簿の品目と内訳の入力用 17 KAKEIBO_INFO* pP0 = NULL; //家計簿.txtの情報を入れるポインタ 18 KAKEIBO_INFO* pP1 = NULL; //メモリ拡張用 19 20 int ret = 0; //読み込み判定 21 int i = 0, j = 0; //繰り返し処理、真偽判定 22 char str[STRSIZE]; //ファイルの文字列の格納 23 int row = 0; //ファイルの行数の格納 24 25 FILE* fp = NULL; 26 27 fopen_s(&fp, "家計簿.txt", "r+"); 28#if 1 29 if (fp != NULL) { 30 while (fgets(str, STRSIZE, fp) != NULL) { //ファイルを開けたら行数を数える。配列要素数になる。 31 printf("[%d] %s", row, str); 32 row++; 33 } 34 fseek(fp, 0, SEEK_SET); 35 } 36 37#endif // 0 38 printf("\n経費の品目の入力 = "); 39 gets_s(info.hinmoku,40); 40 41 printf("金額の入力 = "); 42 scanf_s("%d", &info.price); 43 44#if 1 45 //---------------家計簿.txtがない場合(読み込めない場合)------------------ 46 47 if (fp == NULL) { 48 49 fopen_s(&fp, "家計簿.txt", "w"); //書き込みモード 50 if (fp != NULL) { 51 52 fprintf(fp, "%s %d円", info.hinmoku, info.price); //txtに書き込む 53 printf("保存完了"); 54 fclose(fp); 55 return 0; 56 } 57 58 } 59 60 //----------------家計簿.txtがある場合----------------- 61 62 63 pP0 = (KAKEIBO_INFO*)malloc(row * sizeof(KAKEIBO_INFO)); //pP0[row]分のメモリ格納 64 if (pP0 == NULL) { 65 printf("メモリ確保失敗\n"); 66 exit(0); 67 } 68 69 if (fp == NULL) { 70 printf("読み込み失敗\n"); 71 return 0; 72 } 73 74 while (ret = fscanf(fp, "%s %d円", pP0[i].hinmoku, &pP0[i].price) != EOF) { //ファイルの読み込み(pP0[]に取り込み) 75 if (ret == 0) { 76 printf("読み込みデータなし\n"); 77 return 0; 78 } 79 i++; 80 } 81 82 fseek(fp, 0, SEEK_SET); 83 84 for ( i = 0; i < row; i++) { //新しい品目があるかチェック 85 86 if (strncmp(pP0[i].hinmoku, info.hinmoku, 40) == 0) { //既存の品目に加算 87 pP0[i].price += info.price; 88 j = 0; 89 break; 90 } 91 j++; 92 } 93 94 if (j) { 95 row++; 96 97 pP1 = (KAKEIBO_INFO*)realloc(pP0, row * sizeof(KAKEIBO_INFO)); //メモリを1つ分増やした 98 99 if (pP1 == NULL) { 100 free(pP1); 101 exit(0); 102 } 103 104 for ( i = 0; i < row; i++) { 105 106 if (i == row - 1) { //最後のインデックスに入力を代入 107 pP1[i] = info; 108 } 109 110 printf("[%d]-->%s %d\n", i, pP1[i].hinmoku, pP1[i].price); //書き込み内容表示 111 112 fprintf(fp, "%s %d円\n", pP1[i].hinmoku, pP1[i].price); //書き込み 113 114 } 115 116 fclose(fp); //ファイルを閉じる 117 118 free(pP1); //メモリ解放 119 120 /* 121 free(pP0); 122 */ 123 124 if (pP0 != pP1) { //pP0とpP1アドレスが違ってたら 125 free(pP0); 126 } 127 128 return 0; 129 130 } 131 132 for ( i = 0; i < row; i++) { 133 134 printf("[%d]-->%s %d\n", j, pP0[i].hinmoku, pP0[i].price); //書き込み内容表示 135 136 fprintf(fp, "%s %d円\n", pP0[i].hinmoku, pP0[i].price); //書き込み 137 138 } 139 140 fclose(fp); //ファイルを閉じる 141 142 free(pP0); //メモリ解放 143 144 145#endif // 0 146 147 return 0; 148}

試したこと

配列 動的確保、で検索してほかの解説サイトのプログラムも動かしてみましたが同じエラーが出るもの出ないものがあり違いがわかりませんでした。

メモリ解放について、これはデバッグするとpP0とpP1のアドレスが同じところを指しているので、順番的にpP1を解放して、また同じアドレスのpP0を解放しようとすることで既に解放してあるせいで動作が止まってしまうのかと思います。reallocは別のアドレスを返すことがあるとのことなのでfree(pP0);をいれてましたが、アドレスが同じであればpP1の解放だけで大丈夫かなとおもっております。また対策として127行目のif文をいれました。
この解釈は間違いでしょうか?

補足情報(FW/ツールのバージョンなど)

Windows 10 Home
visualstudio2019 Version 16.8.6

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

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

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

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

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

maisumakun

2021/04/22 07:49

74行目はどの行でしょうか?
退会済みユーザー

退会済みユーザー

2021/04/22 08:07

while (ret = fscanf(fp, "%s %d円", pP0[i].hinmoku, &pP0[i].price) != EOF) { //ファイルの読み込み(pP0[]に取り込み) の行です。
退会済みユーザー

退会済みユーザー

2021/04/22 08:14

//----------------家計簿.txtがある場合-----------------の下のwhileです
退会済みユーザー

退会済みユーザー

2021/04/29 05:55

この場合の警告は読み込むファイル内容によってはオーバーフローする可能性を指摘してるのでしょうか?
guest

回答2

0

pP1 = (KAKEIBO_INFO*)realloc(pP0, row * sizeof(KAKEIBO_INFO)); //メモリを1つ分増やした

reallocでぐぐると解説でてくるので読みましょう
領域の再確保、です。pP0は捨てて、領域を再確保する、という関数です.

投稿2021/04/22 07:52

編集2021/04/22 07:52
y_waiwai

総合スコア87836

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

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

退会済みユーザー

退会済みユーザー

2021/04/22 08:10

ありがとうございます。使い方ばっかりで見てて仕様を見てませんでした。
guest

0

ベストアンサー

憶測ですが警告の原因はファイルの内容がpP0のサイズより多い可能性が想定されているからだと思いました。多い場合、オーバーフローしないように確保したメモリサイズの確認が必要なのかと思いました。そのため、

c

1if (sizeof(pP0) != sizeof(KAKEIBO_INFO)*row) { 2 printf("ERROR"); 3 return 0; 4 }

ifでサイズのチェックをすることで警告が消えました。

投稿2021/04/29 08:45

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2021/04/29 09:31

警告は消えましたが思った通りの動作しなかったです。
dodox86

2021/04/29 09:39 編集

sizeof(pP0) だと、KAKEIBO_INFO* pP0 のポインタ変数のサイズになっちゃいます。C言語の処理系が32ビットなら必ず4、64ビットなら必ず8、です。
退会済みユーザー

退会済みユーザー

2021/04/29 09:53

4bitで出てきました。 今調べてたらmalloc.hでmalloc_usable_sizeという関数があって、それはmallocで確保したメモリサイズを出せるみたいですがLinuxの関数みたいなので、これをvisualstudio仕様にすればいいかと思ってました。
退会済みユーザー

退会済みユーザー

2021/04/29 10:00

テラテイル内の質問の回答で以下があり、 https://teratail.com/questions/119123 取得できないんですね。 とりあえずもっと勉強して試行錯誤します。
退会済みユーザー

退会済みユーザー

2021/04/29 10:27

visualstudioの場合のメモリサイズの取得できました。 https://docs.microsoft.com/ja-jp/previous-versions/visualstudio/visual-studio-2012/z2s077bc(v=vs.110)?redirectedfrom=MSDN <malloc.h>の_msize関数で動的メモリのサイズが見れます。 if (sizeof(pP0) != sizeof(KAKEIBO_INFO)*row) でなく if (_msize(pP0) != sizeof(KAKEIBO_INFO)*row) したのですが警告は消えないです。 それで、 if (sizeof(_msize(pP0)) != sizeof(KAKEIBO_INFO)*row) にしましたが消えないです。
退会済みユーザー

退会済みユーザー

2021/04/29 10:46

見に来た皆様、早とちりで解決済みにしてしまいました。すみません。 無効なデータを読み取っているとのことなので、callocでメモリ確保してみましたが改善しなかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

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

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

質問する

関連した質問