01/10/2018, 00:40

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:

  • 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 #include #include #include

    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é :)
    Nguyen Quang Hien viết 02:51 ngày 01/10/2018

    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…

    Gió viết 02:47 ngày 01/10/2018

    Khi bạn dùng hàm ReadFile thì kết quả đọc được như thế nào?

    Người bí ẩn viết 02:47 ngày 01/10/2018

    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…

    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í:

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

    Ở đâ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 (không biết có hợp lý không =]])

    Khi bạn dùng hàm ReadFile thì kết quả đọc được như thế nào?

    Đọc bình thường bạn, không có lỗi.

    Gió viết 02:49 ngày 01/10/2018

    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ười bí ẩn viết 02:51 ngày 01/10/2018

    Thì việc đọc file có lẽ bị lỗ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

    Việc readfile đọc được vì con trỏ của g chưa bị hủy

    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 ?

    Bạn không thể lưu một con trỏ vào file rồi lấy ra dùng được.

    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 ?

    _g ở đây đọc được vì tất cả con trỏ _g và g giống nhau

    Mình chưa hiểu ý này lắm

    Người bí ẩn viết 02:44 ngày 01/10/2018

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

    Bài liên quan
    0