Hỏi về lỗi khi vào ra file trong C++

a/c cho em hỏi, e chỉ làm vào ra file bình thường nhưng mà lúc chạy nhập xuất xong hết rồi nhưng đến cuối lại báo lỗi has stopped working là như thế nào ạ.

#include<iostream>
#include<string>
#include<fstream>
using namespace std;
class sinhvien{
	private:
		int maso;
		string hoten;
	public:
		void nhap(){
			cout<<"nhap ho ten: "; getline(cin,hoten);
			cout<<"nhap ma so: "; cin>>maso; cin.ignore();
		}
		void xuat(){
			cout<<"ho ten: "<<hoten<<endl;
			cout<<"ma so: "<<maso<<endl;
		}
		void ghitep(){
			fstream ghi("tepx.dat",ios::out|ios::binary);
			ghi.write(reinterpret_cast <const char *>(this),sizeof(sinhvien));
            ghi.close();
		}
		void doctep(){
			fstream doc("tepx.dat",ios::in|ios::binary);
            sinhvien a;
            doc.read(reinterpret_cast <char *>(&a),sizeof(sinhvien));
            a.xuat();
		}
};
main(){
	sinhvien x; 
	x.nhap(); 
	x.ghitep(); 
	cout<<"thong tin doc tu file: "<<endl;
	x.doctep();
	
}

Vẫn chạy bình thường mà bạn. :slight_smile:

Microsoft Windows [Version 10.0.18362.10015]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\Admin> d:

D:\>cd "Visual Studio\VSCode\VSProjects\CPP"

D:\Visual Studio\VSCode\VSProjects\CPP> b check.cpp
g++ "check.cpp" -o "check.exe"

D:\Visual Studio\VSCode\VSProjects\CPP> check
nhap ho ten: 1
nhap ma so: 1
thong tin doc tu file:
ho ten: 1
ma so: 1

D:\Visual Studio\VSCode\VSProjects\CPP> check
nhap ho ten: Sherly
nhap ma so: 1001
thong tin doc tu file:
ho ten: Sherly
ma so: 1001

D:\Visual Studio\VSCode\VSProjects\CPP>

Bạn có chắc đó là lỗi của chương trình.

Nếu vậy thì đặt một vài breakpoint ở chỗ bạn nghi ngờ có bug (chơi lớn thì hàm nào cũng đặt :smile:), step by step rồi xem nó báo bug ở dòng nào là ok. :slight_smile:

2 Likes

bạn thử chạy bằng dev C xem có bị như mình không, mình chạy bằng dev C toàn báo stop không à :frowning:

reinterpret_cast std::string thành byte thì tầm bậy rồi :V std::string có con trỏ trỏ tới chuỗi thực sự nằm trên heap.

  • khi viết sinhvien x; x.nhap(); thì x.hoten sẽ có con trỏ trỏ tới chuỗi nhập vào, vd ở đây là "1" nằm trên heap.
  • khi lưu x.ghitep(); thì địa chỉ của "1" ở trong x.hoten được lưu xuống file
  • khi đọc x.doctep(); thì địa chỉ này được load lên a.hoten, vậy là x.hotena.hoten cùng trỏ tới "1" ở trên heap.
  • khi thoát khỏi x.doctep(), a sẽ bị xóa, trong đó a.hoten sẽ bị xóa, vậy là "1" nằm trên heap đã bị xóa.
  • khi kết thúc chương trình x sẽ bị xóa, tương tự x.hoten cũng bị xóa, nhưng "1" đã bị xóa mất rồi, ở đây lại gọi xóa thêm lần nữa dẫn tới lỗi trên.

nó giống như vầy:

char* hoten1 = new char[10];
// ... lưu giá trị của hoten1, hay là địa chỉ của char[10] kia xuống file
// ... load địa chỉ của char[10] từ file lên
char* hoten2 = hoten1;
delete[] hoten2;
delete[] hoten1; // lỗi: xóa mảng char[10] ở dòng 1 lần thứ 2

thay string hoten thành char hoten[100] chẳng hạn là được :V Xài cin.getline(hoten, 100) thay cho getline(cin, hoten)

7 Likes

Muốn serialize string kiểu binary thì phải tính length và lấy từ buffer ra :smiley:

6 Likes

sở dĩ chạy ko có lỗi có lẽ là do small string optimization của g++ :V hay string dưới 16 ký tự thì con trỏ tới mảng nằm trên heap + biến capacity sẽ biến thành mảng ký tự (8+8 = 16 bytes) :V :V Thử nhập tên 20 ký tự xem có lỗi ko :V

std::basic_string có 3 biến: con trỏ tới mảng động, biến dung lượng của mảng động, và biến kích cỡ của chuỗi. Ví dụ chuỗi 5 ký tự nhưng mảng động có dung lượng là 10 char. Kích thước 3 biến này tùy trình biên dịch, nếu là 64 bit thì có thể sẽ là 8+8+8=24 bytes :V

để tránh cấp phát động cho những chuỗi nhỏ, biến con trỏ và biến dung lượng có thể được “chế” thành mảng char[16], vậy là chứa được chuỗi 16 ký tự :V Để làm điều này có thể sử dụng union :V Ví dụ

union U {
    struct A { char* s; size_t n; } a;
    char s[16]; // hoặc char s[sizeof(char*) + sizeof(size_t)];
} u;

:V :V :V
trong constructor string(const char* s) có thể nó xét strlen của s xem là bao nhiêu, nếu <= 16 thì xài u.s ko cần cấp phát động, nếu > 16 thì cấp phát động cho u.a.s :V :V :V

nếu xài như u.s, thì khi cast string thành byte vẫn đúng, coi như là char[16] và size_t. Load lên lại cũng đúng luôn :V Nhưng khi có cấp phát động thì load vào u.a.s 2 con trỏ trỏ tới cùng 1 mảng trên heap, khi xóa sẽ xóa 2 lần, hoặc tệ hơn khi obj cũ đã bị xóa, vậy là obj mới load lên địa chỉ ma đã bị xóa cũng gây lỗi :V

6 Likes

mình cảm ơn b nha, mình hiểu ý bạn rồi, nhưng mình không hiểu sao phải thay bằng char mới hoạt động được, tại thầy mình bắt khai báo bằng string ý. bạn còn cách fix nào khác không

Như trên :smiley: bạn ko thể thảy nguyên cái std::string xuống được mà phải đọc được buffer của nó, cái này mới có dữ liệu.

6 Likes

vậy thì ghi 4 byte của maso xuống trước, rồi ghi tiếp 8 byte của hoten.size() xuống, rồi ghi tiếp hoten.size() byte của hoten.data()) xuống :V

ghi.write(&maso, sizeof(maso));
size_t sz = hoten.size();
ghi.write(&sz, sizeof(sz));
ghi.write(hoten.data(), sz);

khi đọc lên lại thì đọc 4 byte của maso, rồi 8 byte của hoten.size vào biến tạm sz, rồi lấy giá trị này khởi tạo cho hoten thông qua hoten.resize(sz), sau cùng đọc sz byte vào hoten.data() :V

doc.read(&maso, sizeof(maso));
size_t sz;
doc.read(&sz, sizeof(sz));
hoten.resize(sz); // resize, ko phải reserve :V 
doc.read(&hoten[0], sz); //.data() non-const C++17 mới xài được :V

ghi kiểu này thì mỗi record có size khác nhau, ko truy cập ngẫu nhiên 1 record được :V

6 Likes

được bạn ưiiii :smiley:

C++17 mới được https://en.cppreference.com/w/cpp/string/basic_string/data

Modifying the character array accessed through the const overload of data has undefined behavior.

4 Likes

data() non-const c++17 mới có à :V đã sửa :V

3 Likes

Trong thằng sinhvien có 1 thằng std::string hoten.
Trong thằng hoten ắt hẳn phải có 1 con trỏ và 1 buffer chứa data và 2 thằng này nằm tách biệt nhau.

Khi lưu this xuống nó chỉ lưu con trỏ của hoten mà không lưu dữ liệu của hoten.
Khi backup ngược tại thì đương nhiên hoten bị mất dữ liệu nhưng con trỏ dữ liệu của hoten vẫn còn -> truy cập vào vùng nhớ không thuộc quản lý của hoten.

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