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

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

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

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1244閲覧

C++:動的確保時のメモリリークとValgrindについて

aluminium

総合スコア7

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

メモリリーク

メモリリークは、プログラムファイルがメモリの解放に失敗した時に起こります。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/10/01 08:33

編集2020/10/01 12:50

初心者の学生です。どうぞ宜しくお願いいたします。

動的確保の課題を解いています。
CSVファイルを読み込んで、GPA(成績データ)でソートして表示する課題です。

送信サーバ(Linux)上の Valgrind というメモリリークをチェックするプログラムにより
課題の送信が阻まれております...
C言語のクラスからC++のクラスに進級した途端、これでチェックされるようになり毎回困っています...

お知恵をお借りできれば幸いです。お時間ありがとうございます。

Valgrindによるエラー画面

画面内のエラーメッセージをGoogle翻訳してみましたが、全く意味がわかりません...

  • 条件付きジャンプまたは移動は、初期化されていない値によって異なります
  • エラー:読み取られたレコードの数が正しくありません。 データが破損している可能性があります

イメージ説明

また、Linuxサーバ上でg++してみたところ、

gpaReport.cpp:(.text+0x5): undefined reference to `sdds::load()' gpaReport.cpp:(.text+0xe): undefined reference to `sdds::display()' gpaReport.cpp:(.text+0x13): undefined reference to `sdds::deallocateMemory()'

と出ました。何か手がかりになりますでしょうか...

ソースコード

C++

1#define _CRT_SECURE_NO_WARNINGS 2#define DATAFILE "students.csv" 3#include <iostream> 4#include <cstring> 5#include <cstdio> 6using namespace std; 7 8namespace sdds { 9 struct Student { 10 char* m_name; 11 int m_studentNumber; 12 double m_gpa; 13 }; 14 15 FILE* fptr; 16 int noOfStudents; 17 Student* students; 18 19 // 学生のGPA順に動的配列を並べ替え 20 void sort(); 21 // ファイルから値を含む学生構造をロード 22 bool load(Student& record); 23 // 学生の動的配列を割り当て、すべてのファイルレコードを配列にロード 24 bool load(); 25 // 学生レコードを表示 (for gpaReport.cpp) 26 void display(); 27 // 学生レコードを表示 28 void displayStudentRecord(); 29 // 最初に生徒をソートしてから、すべての生徒を表示 30 void displayAllStudents(); 31 // 配列の割り当てを解除 32 void deallocateMemory(); 33} 34 35namespace sdds { 36 bool openFile(const char filename[]); 37 void closeFile(); 38 int noOfRecords(); 39 bool readName(char name[]); 40 bool readNumber(int *number); 41 bool readGpa(double *gpa); 42} 43 44namespace sdds { 45 void sort() { 46 int i, j; 47 Student temp; 48 for (i = noOfStudents - 1; i > 0; i--) { 49 for (j = 0; j < i; j++) { 50 if (students[j].m_gpa > students[j + 1].m_gpa) { 51 temp = students[j]; 52 students[j] = students[j + 1]; 53 students[j + 1] = temp; 54 } 55 } 56 } 57 } 58 59 // ファイルから1つの学生レコードを読み取り、学生参照引数にロード 60 bool load(Student& record) { 61 bool ok = false; 62 char name[128]; 63 // 名前の読み取りが成功した場合 64 if (readName(name)) { 65 ok = readNumber(&record.m_studentNumber) && readGpa(&record.m_gpa); 66 if (ok) { 67 int nameSize = 0; 68 nameSize = strlen(name) + 1; // 名前のサイズ+1にメモリを割り当てます 69 record.m_name = new char[nameSize]; // アドレスを参照の名前で保持 70 strcpy(record.m_name, name ); // 名前を新しく割り当てられたメモリにコピー 71 ok = true; // 問題なければ okフラグをtrueに設定 72 } 73 } 74 return ok; 75 } 76 77 // the 0 arg load function 78 bool load() { 79 bool ok = false; 80 int i = 0; 81 if (openFile(DATAFILE)) { 82 noOfStudents = noOfRecords(); // ファイル内のレコード数に設定 83 84 // 学生の配列をグローバルに動的に割り当て 85 students = new Student[noOfStudents]; 86 87 for (i = 0; i < noOfStudents; i++) {// ループ内で学生レコードをファイルから動的配列にロード 88 bool res = load(students[i]); 89 if (res == false) { 90 break; 91 } 92 } 93 // レコードの数が読み取り数と一致しない場合、エラーメッセージを出力 94 if (i != noOfStudents){ 95 cout << "Error: incorrect number of records read; the data is possibly corrupted" << endl; 96 } 97 else { 98 ok = true; 99 } 100 closeFile(); 101 } 102 else { 103 cout << "Could not open data file: " << DATAFILE<< endl; 104 } 105 return ok; 106 } 107 108 // 学生レコードを表示 (for gpaReport.cpp) 109 void display() { 110 displayAllStudents(); 111 } 112 113 // 学生レコードを表示 114 void displayStudentRecord(const Student *record) { 115 cout << record->m_name << ", " 116 << record->m_studentNumber << ": " 117 << record->m_gpa << endl; 118 } 119 120 // 最初に生徒をソートしてから、すべての生徒を表示 121 void displayAllStudents() { 122 int i; 123 sort(); 124 for (i = 0; i < noOfStudents; i++) { 125 cout << i+1 << ": "; 126 displayStudentRecord(&students[i]); 127 } 128 } 129 130 // 最初にstudent要素のすべての名前の割り当てを解除し、次にstudent配列の割り当てを解除 131 void deallocateMemory() { 132 if (noOfStudents <= 0 || students == NULL) { 133 return; 134 } 135 // ループしてstudents配列のすべての要素を調べ、各学生の動的名の割り当てを解除 136 for (int i = 0; i < noOfStudents; i++) { 137 delete [] students[i].m_name; 138 students[i].m_name = nullptr; 139 } 140 delete [] students; // students配列全体の割り当てを解除 141 students = nullptr; 142 } 143} 144 145namespace sdds { 146 bool openFile(const char filename[]) { 147 fptr = fopen(filename, "r"); 148 return fptr != NULL; 149 } 150 int noOfRecords() { 151 int noOfRecs = 0; 152 char ch; 153 while (fscanf(fptr, "%c", &ch) == 1) { 154 noOfRecs += (ch == '\n'); 155 } 156 rewind(fptr); 157 return noOfRecs + 1; // 最終行用に +1 158 } 159 void closeFile() { 160 if (fptr) fclose(fptr); 161 } 162 163 bool readName(char name[]) { // データファイル内の学生名の読み取り 164 char names[256]; 165 int gi = 0; //names index; 166 int i = 0;// name[i] index 167 bool res = fscanf(fptr, "%[^,],", names); 168 if (res) { //csvを文字列の配列に拡散する 169 res = false; 170 while (names[gi]) { 171 name[i++] = names[gi++]; 172 res = true; 173 } 174 } 175 name[i++] = '\0'; 176 name[i] = '\0'; // 文字列の配列を終了する 177 return res; 178 } 179 180 bool readNumber(int *number) { // データファイル内の学生番号の読み取り 181 return fscanf(fptr, "%d,", number) == 1; 182 } 183 184 bool readGpa(double *gpa) { // データファイル内のGPAの読み取り 185 return fscanf (fptr, "%lf\n", gpa) == 1; 186 } 187 188} 189 190using namespace sdds; 191 192int main() { 193 if (load()) { 194 display(); 195 } 196 deallocateMemory(); 197 return 0; 198}

データファイル

CSV

1Abraham Simpson,324543,3.9 2Alice Glick,459608,1.9 3Allison Taylor,747954,3.2 4Apu Nahasapeemapetilon,290816,2.6 5Bart Simpson,753102,3.9 6Bernice Hibbert,242653,3.8 7Carl Carlson,241968,3.7 8Homer Simpson,413084,2.9 9Lisa Simpson,693664,4.0 10Luann Van Houten,737447,2.3 11Martin Prince,575687,3.5 12Maude Flanders,272754,1.7 13Sarah Wiggum,920562,2.0 14Surly Duff,146127,2.6

試したこと

  • new の格納先を +1 してみた --> 何も変わりませんでした(追加前と同じエラー)
  • delete を追加してみた --> 余計にエラーが増えました...
  • コマンドで'valgrind result' と打っても 'command not found' と言われてしまい、行番号すらわかりませんでした...

ご回答をいただき追加で試したこと

  • delete [] とした(投稿時のスクショは [] ありのもので、投稿したコードが古かったです、すみません)
  • students[i].m_name と students に nullptr を追加

上記を試してみましたが、Valgrindからは同じ出力が出ます...
イメージ説明

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

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

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

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

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

SHOMI

2020/10/01 16:02

> students[i].m_name と students に nullptr を追加 なぜ解放後に…
guest

回答1

0

ベストアンサー

C++

1delete students[i].m_name; 2delete students; // students配列全体の割り当てを解除

delete[]じゃないの?


C++

1 for (int i = 0; i < noOfStudents; i++) { 2 delete students[i].m_name; 3 }

レコード数を読めたけれど、名前を読めなかった場合それ以降の要素のm_nameは不定値なのでdeleteしてはまずいのでは?
Studentにコンストラクタをもたせるか、students = new Student[noOfStudents];の後で全要素nullptrで初期化するべき。


画面内のエラーメッセージをGoogle翻訳してみましたが、全く意味がわかりません...

エラー:読み取られたレコードの数が正しくありません。 データが破損している可能性があります

あなたの書いたプログラムが出しているメッセージですが…

C++

1 // レコードの数が読み取り数と一致しない場合、エラーメッセージを出力 2 if (i != noOfStudents){ 3 cout << "Error: incorrect number of records read; the data is possibly corrupted" << endl; 4 }

Conditional jump or move depends on uninitialised value(s)

ファイルを正しく読めておらず未初期化の変数nameswhileの条件に使用して怒られています。
以下を直せば今指摘されているValgrindのエラーは消えます。

diff

1 struct Student { 2 char* m_name; 3 int m_studentNumber; 4 double m_gpa; 5+ Student() : m_name( nullptr){} 6 }; 7 8- Student* students; 9+ Student* students = nullptr; 10 11 bool readName(char name[]) { // データファイル内の学生名の読み取り 12 char names[256]; 13 int gi = 0; //names index; 14 int i = 0;// name[i] index 15- bool res = fscanf(fptr, "%[^,],", names); 16+ bool res = 1 == fscanf(fptr, "%[^,],", names); 17 if (res) { //csvを文字列の配列に拡散する 18 res = false; 19 while (names[gi]) { 20 name[i++] = names[gi++]; 21 res = true; 22 } 23 } 24 name[i++] = '\0'; 25 name[i] = '\0'; // 文字列の配列を終了する 26 return res; 27 }

students.csvが14行にも関わらずnoOfRecords()では15が返り、
15行目を読もうとして上記ValgrindのメッセージとError: incorrect number of records read; the data is possibly corruptedが出力されているのでしょう。

diff

1 int noOfRecords() { 2 int noOfRecs = 0; 3 char ch; 4 while (fscanf(fptr, "%c", &ch) == 1) { 5 noOfRecs += (ch == '\n'); 6 } 7 rewind(fptr); 8- return noOfRecs + 1; // 最終行用に +1 9+ return noOfRecs; 10 }

投稿2020/10/01 08:42

編集2020/10/01 17:30
SHOMI

総合スコア4079

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

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

aluminium

2020/10/01 08:44

ありがとうございます。 new した所、全部に delete を入れてみましたが、余計にエラーが出てしまったんです...
SHOMI

2020/10/01 08:52

>new した所、全部に delete を入れてみました どこにどう入れたんですか? コードを理解した上で入れましょう
cateye

2020/10/01 10:47 編集

deleteとdelete[]は違いますよ。→aluminiumさん
aluminium

2020/10/01 12:59

> SHOMI 様 わかりづらく、申し訳ありませんでした。 85行目の students = new Student[noOfStudents]; 69行目の record.m_name = new char[nameSize]; に対応するように入れてみましたが、結果は同じでした。 nullptr も 追加してみましたが、結果は同じでした。 コンストラクタ とは何か調べてみたのですが、よくわからず... どう実装したらいいでしょうか?
aluminium

2020/10/01 12:59

> cateye 様 ありがとうございます。
aluminium

2020/10/01 21:01 編集

仰るとおり、return noOfRecs; に +1 してしまっていました... これが原因でした!! 別のコンパイラで動かしてみたとき、最終行が上手く出力されず、 安易に +1 してしまったのがいけなかったです。 SHOMIさんのコメント「おそらくstudents.csvが14行にも関わらずnoOfRecords()では15が返り」 本当に助かりました!また、色々試してアドバイスをいただき、ありがとうございました!! 最初から課題送信と同じサーバ上でコンパイルするべきですね... 勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問