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

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

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

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

Q&A

解決済

2回答

6759閲覧

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

gasto

総合スコア1

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行目はどの行でしょうか?
gasto

2021/04/22 08:07

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

2021/04/22 08:14

//----------------家計簿.txtがある場合-----------------の下のwhileです
gasto

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

総合スコア87774

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

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

gasto

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

gasto

総合スコア1

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

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

gasto

2021/04/29 09:31

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

2021/04/29 09:39 編集

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

2021/04/29 09:53

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

2021/04/29 10:00

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

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) にしましたが消えないです。
gasto

2021/04/29 10:46

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問