Lỗi khi giải phóng con trỏ
Hi mọi người,
Mình có 1 bài tập như sau:
Nhập vào danh sách các sinh viên trong 1 lớp học biết thông tin mỗi sinh viên gồm có: họ tên, mã số, điểm thi các môn tự chọn. Yêu cầu: Ghi danh sách lớp học này xuống file nhị phân theo thứ tự bảng chữ cái Alphabet.
Đọc danh sách lớp học từ file nhị phân đã được tạo ra ở bài tập 1, yêu cầu: Sắp xếp danh sách lớp tăng/giảm dần theo điểm trung bình, tìm thông tin sinh viên có điểm trung bình cao nhất/thấp nhất rồi in lên màn hình.
Mình tạo 3 struct:
Còn đây là source code:
typedef struct subject SJ;
struct subject
{
std::string subject_name; // tên môn học
double mark; // điểm của môn học
};
std::istream& operator >> (std::istream &, SJ *);
std::istream& operator >> (std::istream &, SJ &);
std::ostream& operator << (std::ostream &, SJ *);
std::ostream& operator << (std::ostream &, SJ &);
<li>**SubJect.cpp** (xử lý thông tin)</li>
#include “SubJect.h”
std::istream& operator >> (std::istream &is, SJ *s)
{
std::cout << "Nhap ten mon hoc: ";
while (getchar() != ‘
’ && getchar() != EOF) {}
std::getline(is, s->subject_name);
std::cout << "Nhap diem mon hoc: ";
is >> s->mark;
return is;
}
std::istream& operator >> (std::istream &is, SJ &s)
{
std::cout << "Nhap ten mon hoc: ";
while (getchar() != ‘
’ && getchar() != EOF) {}
std::getline(is, s.subject_name);
std::cout << "Nhap diem mon hoc: ";
is >> s.mark;
return is;
}
std::ostream& operator << (std::ostream &os, SJ *s)
{
os << "Ten mon hoc: " << s->subject_name << std::endl;
os << "Diem mon hoc: " << s->mark << std::endl;
return os;
}
std::ostream& operator << (std::ostream &os, SJ &s)
{
os << "Ten mon hoc: " << s.subject_name << std::endl;
os << "Diem mon hoc: " << s.mark << std::endl;
return os;
}
<li>**StuDent.h** (lưu thông tin của 1 học sinh)</li>
#pragma once
#include “SubJect.h”;
typedef struct student STD;
struct student
{
std::string name;
std::string code;
int n_subjects;
SJ *sjs; // mảng sjs kiểu SJ để lưu thông tin nhiều môn học
};
std::istream& operator >> (std::istream &, STD *);
std::istream& operator >> (std::istream &, STD &);
std::ostream& operator << (std::ostream &, STD *);
std::ostream& operator << (std::ostream &, STD &);
<li>**StuDent.cpp** (xử lý thông tin)</li>
#include “StuDent.h”;
std::istream& operator >> (std::istream &is, STD *s)
{
std::cout << "
Nhap ho va ten: ";
while (getchar() != ‘
’ && getchar() != EOF) {}
std::getline(is, s->name);
std::cout << "
Nhap ma so: ";
is >> s->code;
std::cout << "
Nhap so luong mon hoc: ";
is >> s->n_subjects;
std::cout << “
---------- Nhap danh sach mon hoc ---------- ” << std::endl;
s->sjs = new SJ[s->n_subjects];
for (int i = 0; i < s->n_subjects; ++i)
{
std::cout << "
Nhap thong tin mon " << i + 1 << “:
”;
is >> s->sjs[i];
}
return is;
}
std::istream& operator >> (std::istream &is, STD &s)
{
std::cout << "
Nhap ho va ten: ";
while (getchar() != ‘
’ && getchar() != EOF) {}
std::getline(is, s.name);
std::cout << "
Nhap ma so: ";
is >> s.code;
std::cout << "
Nhap so luong mon hoc: ";
is >> s.n_subjects;
std::cout << “
---------- Nhap danh sach mon hoc ---------- ” << std::endl;
s.sjs = new SJ[s.n_subjects];
for (int i = 0; i < s.n_subjects; ++i)
{
std::cout << "
Nhap thong tin mon " << i + 1 << “:
”;
is >> s.sjs[i];
}
return is;
}
std::ostream& operator << (std::ostream &os, STD *s)
{
os << "
Ho va ten: " << s->name << std::endl;
os << "
Ma so: " << s->code << std::endl;
os << “
---------- Danh sach mon hoc ----------
”;
for (int i = 0; i < s->n_subjects; ++i)
{
os << "
Mon " << i + 1 << “:
”;
os << s->sjs[i];
}
return os;
}
std::ostream& operator << (std::ostream &os, STD &s)
{
os << "
Ho va ten: " << s.name << std::endl;
os << "
Ma so: " << s.code << std::endl;
os << “
---------- Danh sach mon hoc ----------
”;
for (int i = 0; i < s.n_subjects; ++i)
{
os << "
Mon " << i + 1 << “:
”;
os << s.sjs[i];
}
return os;
}
<li>**Grade.h** (lưu thông tin 1 lớp học)</li>
#pragma once
#include “StuDent.h”;
typedef struct grade GRD;
struct grade
{
int n_students;
STD *students;
};
std::istream& operator >> (std::istream &, GRD *);
std::istream& operator >> (std::istream &, GRD &);
std::ostream& operator << (std::ostream &, GRD *);
std::ostream& operator << (std::ostream &, GRD &);
<li>**Grade.cpp** (xử lý thông tin)</li>
#include “Grade.h”
std::istream& operator >> (std::istream &is, GRD *g)
{
std::cout << "Nhap so luong hoc sinh: ";
is >> g->n_students;
g->students = new STD[g->n_students];
for (int i = 0; i < g->n_students; ++i)
{
std::cout << “
------------ NHAP THONG TIN HOC SINH " << i + 1 << " ------------
”;
is >> g->students[i];
}
return is;
}
std::istream& operator >> (std::istream &is, GRD &g)
{
std::cout << "Nhap so luong hoc sinh: ";
is >> g.n_students;
g.students = new STD[g.n_students];
for (int i = 0; i < g.n_students; ++i)
{
std::cout << “
------------ NHAP THONG TIN HOC SINH " << i + 1 << " ------------
”;
is >> g.students[i];
}
return is;
}
std::ostream& operator << (std::ostream &os, GRD *g)
{
for (int i = 0; i < g->n_students; ++i)
{
os << “
------------ THONG TIN HOC SINH " << i + 1 << " ------------
”;
os << g->students[i];
}
return os;
}
std::ostream& operator << (std::ostream &os, GRD &g)
{
for (int i = 0; i < g.n_students; ++i)
{
os << “
------------ THONG TIN HOC SINH " << i + 1 << " ------------
”;
os << g.students[i];
}
return os;
}
<li>**Còn đây là file source.cpp**</li>
#include “Grade.h”;
void WriteFile(std::fstream &, GRD *); // đọc thông tin từ struct vào file nhị phân
void SortList(GRD *); // sắp xếp danh sách lớp học theo thứ tự Alphabet
std::string CutStrName(std::string); // lấy ra tên chính trong 1 tên
void ReadFile(std::fstream &, GRD *); // đọc thông tin từ file nhị phân vào biến struct
bool Bigger(double , double ); // con trỏ hàm
bool Smaller(double , double ); // con trỏ hàm
double AverageMark(GRD *, int ); // trả về điểm trung bình của 1 học sinh có chỉ số là tham số index của hàm
void SortList_AverageMark(GRD , bool()(double, double)); // sắp xếp danh sách lớp tăng/giảm theo điểm trung bình
int PrntInf_BestAvrgMark(GRD g, bool()(double, double)); // xuất thông tin của học sinh có điểm trung bình cao/thấp nhất
void WriteFile(std::fstream &FileOut, GRD *g)
{
std::cin.ignore();
FileOut.write((char *)g, sizeof(GRD));
}
std::string CutStrName(std::string str) // This function gets the last name of a name
{
int length = str.length();
std::string string;
for (int i = length - 1; i >= 0; --i)
{
if (str.at(i) != ’ ')
string.push_back(str.at(i));
else
break;
}
std::reverse(std::begin(string), std::end(string));
return string;
}
void SortList(GRD *g)
{
for (int i = 0; i < g->n_students - 1; ++i)
{
for (int j = i + 1; j < g->n_students; ++j)
{
if (CutStrName(g->students[i].name) > CutStrName(g->students[j].name))
std::swap(g->students[i], g->students[j]);
}
}
}
void ReadFile(std::fstream &FileIn, GRD *_g)
{
FileIn.read((char *)_g, sizeof(GRD));
}
bool Bigger(double a, double b)
{
return a > b;
}
bool Smaller(double a, double b)
{
return a < b;
}
double AverageMark(GRD *g, int index)
{
double sum = 0;
double count = 0;
for (int i = 0; i < g->students[index].n_subjects; ++i)
{
sum += g->students[index].sjs[i].mark;
++count;
}
return sum / count;
}
void SortList_AverageMark(GRD *g, bool(*ptr)(double, double))
{
for (int i = 0; i < g->n_students - 1; ++i)
{
for (int j = i + 1; j < g->n_students; ++j)
{
if (ptr(AverageMark(g, i), AverageMark(g, j)))
std::swap(g->students[i], g->students[j]);
}
}
}
int PrntInf_BestAvrgMark(GRD *g, bool(*ptr)(double, double))
{
double MaxMin = g->students[0].sjs[0].mark;
int index = 0;
for (int i = 0; i < g->n_students; ++i)
{
if (ptr(AverageMark(g, i), MaxMin))
index = i;
}
return index;
}
int main()
{
GRD *g = new GRD;
std::cin >> g;
//std::cout << g;
SortList(g);
std::fstream FileOut("DanhSachLopHoc.doc", std::ios_base::out | std::ios_base::binary);
WriteFile(FileOut, g);
FileOut.close();
GRD *_g = new GRD;
FileOut.open("DanhSachLopHoc.doc", std::ios::in | std::ios::binary);
ReadFile(FileOut, _g);
FileOut.close();
std::cout << _g;
std::cout << "
DANH SACH LOP HOC SAU KHI SAP XEP THEO YEU CAU
";
SortList_AverageMark(_g, Smaller);
std::cout << _g;
std::cout << "
THONG TIN SINH VIEN CO DTB THEO YEU CAU
";
std::cout << _g->students[PrntInf_BestAvrgMark(g, Bigger)] << std::endl;
/*
delete[] _g->students->sjs;
delete[] _g->students;
delete _g;
delete[] g->students->sjs;
delete[] g->students;
delete g;
*/
system("pause");
return 0;
}
Vấn đề là ở file **souce.cpp**, khi mình giải phóng con trỏ ở dưới thì nó báo lỗi, cụ thể là ở thằng `delete[] g->students->sjs;` ấy:
<img src="https://daynhauhoc.com//daynhauhoc.s3-ap-southeast-1.amazonaws.com/original/3X/3/3/33a94c14e9a9268e8a0545b38e7fc4831c028982.png" width="690" height="387">
Mọi người ai biết lỗi này giúp mình nhé. Xin cảm ơn !
P/S: Tuy source code có hơi lằng nhằng và dài dòng nhưng mình đã chú thích hết cỡ rồi. Mong mọi người chịu khó nhé :)
bạn giải phóng sai địa chỉ, vi phạm vào vùng nhớ trên ram của app khác , nên windows nó báo lỗi.
Giải quyết: Chạy debug từng dòng một, watch thật kĩ các địa chỉ con trỏ mà mình tạo new hay release…
Khi bạn dùng hàm ReadFile thì kết quả đọc được như thế nào?
Mình debug rồi bạn.
Thật tình mà nói, cái này nó hơi khó hiểu. Vì sao thì chúng mình quan sát 1 tí:
Ở đây, 2 biến con trỏ
_g
vàg
đều có dữ liệu như nhau (do 1 thằng nhập từ console, còn 1 thằng nhập từ file) nên chắc chắn các member nó cũng như nhau.Vậy sao nó lại báo lỗi ở chỗ giải phóng con trỏ
g
trong khi con trỏ_g
nó lại không báo lỗi ?Đấy là mấu chốt đấy bạn (không biết có hợp lý không =]])
Đọc bình thường bạn, không có lỗi.
Giả sử bạn bỏ đoạn này
Thì việc đọc file có lẽ bị lỗi. Việc readfile đọc được vì con trỏ của g chưa bị hủy. Bạn không thể lưu một con trỏ vào file rồi lấy ra dùng được. _g ở đây đọc được vì tất cả con trỏ _g và g giống nhau. Nên khi delete chỉ cần delete của 1 cái.
Đúng rồi. Vì nếu bỏ đoạn đấy thì coi như file
DanhSachLopHoc.doc
chưa có dữ liệu thì không thể nào đọc vào con trỏ_g
Uhm …
Mình thử bỏ cái đoạn
lên trước khi khởi tạo con trỏ
_g
như sau:thì chương trình sẽ lỗi ở dòng
std::cout << _g;
Mình debug thử thì khi debug qua dòng
ReadFile(FileOut, _g);
thì con trỏ_g
chỉ có dữ liệu cho membern_students
. Còn mảngstudents
trong_g
không có nên khi xuất bị lỗiCái này có liên quan đến vấn đề bạn nói không ?
Mình không con lựa chọn nào khác. Vì tập tin nhị phân bị “hạn chế” hơn tập tin văn bản, nên buộc phải ghi luôn con trỏ
g
vào file bằng<file name>.write();
, chứ nếu không làm vậy thì còn cách ghi nào khác không ? Nếu có thì lúc đọc (read) ra sẽ như thế nào ?Mình chưa hiểu ý này lắm
Trả lời giùm mình mấy câu hỏi trên đi.