Lỗi khi giải phóng con trỏ

Hi mọi người,

Mình có 1 bài tập như sau:

Mình tạo 3 struct:

  • 1 struct lưu thông tin của môn học gồm Tên môn và điểm môn.
  • 1 struct lưu thông tin của học sinh gồm Họ & tên, mã số và danh sách môn học
  • 1 struct lưu thông tin của lớp học gồm Số học sinh và thông tin mỗi học sinh
  • Còn đây là source code:

  • **SubJect.h** (lưu thông tin của môn học)
  • #pragma once
    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <fstream>
    
    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 &);
    
  • **SubJect.cpp** (xử lý thông tin)
  • #include "SubJect.h"
    
    std::istream& operator >> (std::istream &is, SJ *s)
    {
    	std::cout << "Nhap ten mon hoc: ";
    	while (getchar() != '\n' && 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() != '\n' && 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;
    }
    
  • **StuDent.h** (lưu thông tin của 1 học sinh)
  • #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 &);
    
  • **StuDent.cpp** (xử lý thông tin)
  • #include "StuDent.h";
    
    std::istream& operator >> (std::istream &is, STD *s)
    {
    	std::cout << "\nNhap ho va ten: ";
    	while (getchar() != '\n' && getchar() != EOF) {}
    	std::getline(is, s->name);
    	std::cout << "\nNhap ma so: ";
    	is >> s->code;
    	std::cout << "\nNhap so luong mon hoc: ";
    	is >> s->n_subjects;
    	std::cout << "\n\t---------- Nhap danh sach mon hoc ----------\t" << std::endl;
    	s->sjs = new SJ[s->n_subjects];
    	for (int i = 0; i < s->n_subjects; ++i)
    	{
    		std::cout << "\nNhap thong tin mon " << i + 1 << ":\n";
    		is >> s->sjs[i];
    	}
    	return is;
    }
    std::istream& operator >> (std::istream &is, STD &s)
    {
    	std::cout << "\nNhap ho va ten: ";
    	while (getchar() != '\n' && getchar() != EOF) {}
    	std::getline(is, s.name);
    	std::cout << "\nNhap ma so: ";
    	is >> s.code;
    	std::cout << "\nNhap so luong mon hoc: ";
    	is >> s.n_subjects;
    	std::cout << "\n\t---------- Nhap danh sach mon hoc ----------\t" << std::endl;
    	s.sjs = new SJ[s.n_subjects];
    	for (int i = 0; i < s.n_subjects; ++i)
    	{
    		std::cout << "\nNhap thong tin mon " << i + 1 << ":\n";
    		is >> s.sjs[i];
    	}
    	return is;
    }
    std::ostream& operator << (std::ostream &os, STD *s)
    {
    	os << "\nHo va ten: " << s->name << std::endl;
    	os << "\nMa so: " << s->code << std::endl;
    	os << "\n\t---------- Danh sach mon hoc ----------\t\n";
    	for (int i = 0; i < s->n_subjects; ++i)
    	{
    		os << "\nMon " << i + 1 << ":\n";
    		os << s->sjs[i];
    	}
    	return os;
    }
    std::ostream& operator << (std::ostream &os, STD &s)
    {
    	os << "\nHo va ten: " << s.name << std::endl;
    	os << "\nMa so: " << s.code << std::endl;
    	os << "\n\t---------- Danh sach mon hoc ----------\t\n";
    	for (int i = 0; i < s.n_subjects; ++i)
    	{
    		os << "\nMon " << i + 1 << ":\n";
    		os << s.sjs[i];
    	}
    	return os;
    }
    
  • **Grade.h** (lưu thông tin 1 lớp học)
  • #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 &);
    
  • **Grade.cpp** (xử lý thông tin)
  • #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 << "\n\t------------ NHAP THONG TIN HOC SINH " << i + 1 << " ------------\t\n";
    		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 << "\n\t------------ NHAP THONG TIN HOC SINH " << i + 1 << " ------------\t\n";
    		is >> g.students[i];
    	}
    	return is;
    }
    std::ostream& operator << (std::ostream &os, GRD *g)
    {
    	for (int i = 0; i < g->n_students; ++i)
    	{
    		os << "\n\t------------ THONG TIN HOC SINH " << i + 1 << " ------------\t\n";
    		os << g->students[i];
    	}
    	return os;
    }
    std::ostream& operator << (std::ostream &os, GRD &g)
    {
    	for (int i = 0; i < g.n_students; ++i)
    	{
    		os << "\n\t------------ THONG TIN HOC SINH " << i + 1 << " ------------\t\n";
    		os << g.students[i];
    	}
    	return os;
    }
    
  • **Còn đây là file source.cpp**
  • #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 << "\n\t DANH SACH LOP HOC SAU KHI SAP XEP THEO YEU CAU \t\n";
    	SortList_AverageMark(_g, Smaller);
    	std::cout << _g;
    
    	std::cout << "\n\t THONG TIN SINH VIEN CO DTB THEO YEU CAU \t\n";
    	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:

    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é :slight_smile:

    1 Like

    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ỏ _gg đề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 :slight_smile: (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

            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();
    

    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

    delete[] g->students->sjs;
    delete[] g->students;
    delete g;
    

    lên trước khi khởi tạo con trỏ _g như sau:

    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();
    
    	delete[] g->students->sjs;
    	delete[] g->students;
    	delete g;
    
    	GRD *_g = new GRD;
    	FileOut.open("DanhSachLopHoc.doc", std::ios::in | std::ios::binary);
    	ReadFile(FileOut, _g);
    	FileOut.close();
    	std::cout << _g;
    

    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 member n_students. Còn mảng students trong _g không có nên khi xuất bị lỗi

    Cá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 :slight_smile:

    Trả lời giùm mình mấy câu hỏi trên đi.

    83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?