0

So I'm making basic CRUD

create work fine

but when the code reach file.read(code)

VS display Read Access Violation

When I try to run each line 1 by 1 in read function there's no error until I reach file.read I'm not able to figure out the causes

I suspect the problem is in here:

Mahasiswa read(fstream &file, int pos) {
    Mahasiswa result;
    file.open("data.bin", ios::in | ios::binary);
    file.seekp(pos * sizeof(Mahasiswa));
    file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));
    file.close();
    return result;
}

This is the entire code:

#include <fstream>
#include <iostream>
#include <string>
using namespace std;

struct Mahasiswa {
    int no;
    int pk;
    string nim;
    string nama;
    string jurusan;
};

void create(fstream &file, int no, int pk, string nim, string nama, string jurusan) {
        file.open("data.bin", ios::app | ios::out | ios::binary);
        Mahasiswa mhs;
        mhs.no = no;
        mhs.pk = pk;
        mhs.nim = nim;
        mhs.nama = nama;
        mhs.jurusan = jurusan;
        file.write(reinterpret_cast<char*>(&mhs), sizeof(Mahasiswa));
        file.close();
}

Mahasiswa read(fstream &file, int pos) {
    Mahasiswa result;
    file.open("data.bin", ios::in | ios::binary);
    file.seekp(pos * sizeof(Mahasiswa));
    file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));
    file.close();
    return result;
}

int main()
{   
    fstream file;
    create(file, 1, 12, "0123", "Person", "TIK");
    Mahasiswa til = read(file, 0);

    cout << til.nama;
}

source code from the YouTuber:

Apparently this code works:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct Mahasiswa {
    int NIM;
    string nama;
    string jurusan;
};

Mahasiswa ambilData(int posisi, fstream& myFile) {
    Mahasiswa bufferData;

    myFile.seekp((posisi - 1) * sizeof(Mahasiswa));
    myFile.read(reinterpret_cast<char*>(&bufferData), sizeof(Mahasiswa));

    return bufferData;
}

void menulisData(Mahasiswa& data, fstream& myFile) {
    myFile.write(reinterpret_cast<char*>(&data), sizeof(Mahasiswa));
}

void menulisDataByPos(int posisi, Mahasiswa& bufferData, fstream& myFile) {
    myFile.seekg((posisi - 1) * sizeof(Mahasiswa));
    myFile.write(reinterpret_cast<char*>(&bufferData), sizeof(Mahasiswa));
}

int main() {
    fstream myFile;
    myFile.open("data.bin", ios::trunc | ios::out | ios::in | ios::binary);

    Mahasiswa mahasiswa1, mahasiswa2, mahasiswa3, output;

    mahasiswa1.NIM = 123;
    mahasiswa1.nama = "ucup";
    mahasiswa1.jurusan = "memasak";

    mahasiswa2.NIM = 124;
    mahasiswa2.nama = "otong";
    mahasiswa2.jurusan = "menjahit";

    mahasiswa3.NIM = 125;
    mahasiswa3.nama = "sandra";
    mahasiswa3.jurusan = "mesin";

    menulisData(mahasiswa1, myFile);
    menulisData(mahasiswa2, myFile);
    menulisData(mahasiswa3, myFile);

    mahasiswa2.nama = "mario";
    menulisDataByPos(2, mahasiswa2, myFile);

    output = ambilData(2, myFile);

    cout << output.NIM << endl;
    cout << output.nama << endl;
    cout << output.jurusan << endl;

    myFile.close();
    cin.get();
    return 0;
}
  • 2
    You cannot serialize `std::string` by just writing its bytes to a file. `std::string` contains pointers to the actual string somewhere in memory. – Yksisarvinen Jun 09 '21 at 08:32
  • you should consider using a serialization library boost for example is excellent – Alessandro Teruzzi Jun 09 '21 at 08:34
  • @AlessandroTeruzzi is there any way to serialize it without lib you mentioned? –  Jun 09 '21 at 08:39
  • Seems you want to invent your serialize format. It's not an easy task. – prehistoricpenguin Jun 09 '21 at 08:42
  • @Vaness sure, but it is tricky, any reason why you cannot use boost? it is pretty much as good (and common) as the standard library – Alessandro Teruzzi Jun 09 '21 at 08:43
  • @AlessandroTeruzzi I was following basic create and read. this youtuber was able to store struct with string inside to bin file when I try it doesn't work as expected. –  Jun 09 '21 at 08:45
  • 3
    even a youtuber cannot write a `std::string` to a file like that. Did they perhaps write an array of characters to the file? – 463035818_is_not_an_ai Jun 09 '21 at 08:47
  • @463035818_is_not_a_number no I could link the video but the video was in my language if you don't mind. –  Jun 09 '21 at 08:55
  • there could be some nuances like he didn't actually used `std::string` but some other `string` that supports serialization somehow, e.g. being a struct with an array. Or he used managed C++. Or some oddball implementation where it accidently works by exploiting UB if data was still in memory. Or something. – Swift - Friday Pie Jun 09 '21 at 09:02
  • @Swift-FridayPie He was using macOS is there any chance that it might be different? –  Jun 09 '21 at 09:05
  • @Vaness it's just an os, there is still question of what toolchain he used. but if he used a static object in his code , memory could be left untouched and not freed, then this accidently works. Or his strings were so short that they could use optimization that stores 8-12 bytes in structure itself (it's unspecified but allowed) – Swift - Friday Pie Jun 09 '21 at 09:12

2 Answers2

0

From language lawyer's point of view an UB happens here:

file.write(reinterpret_cast<char*>(&mhs), sizeof(Mahasiswa));

and here:

file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));

What happens here is an equivalent of memcpy. memcpy is an equivalent of shallow copy, but can be used only on so-called "POD" types, which are:

a) trivially constructible - no user-defined default constructor;

b) trivially copyable - no user-defined copy constructor or operator;

c) standard memory layout - no virtual inheritance, virtual members, const non-static members, etc.

std::string isn't a aggregate and isn't a POD, it stores a pointer to separate memory storage where actually string data resides, so it cannot be shallow copied and got special members defined to manage that. As those are members, i.e. subobjects of Mahasiswa, Mahasiswa cannot be copied that way either. Rare case when std::string can be memcpy-ed is when it uses short string optimization, which is hard to predict in portable way.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Hey, I've found the source code where it stores string inside a struct. included at the bottom edited section –  Jun 09 '21 at 15:33
  • 1
    @Vaness yeah, the thing is that the allowed size for that does vary from compiler to compiler (and for different runtime libraries for same compiler even) and happens only if string was initialized like that, afaik. `std::string` in this case treats itself as an array of chars with leaving some magic number as a flag that it is a "short" string" – Swift - Friday Pie Jun 09 '21 at 16:19
0

I finally found an answer to this question. I just need to close and reopen the stream

Here is the explanation why I do this https://stackoverflow.com/a/32056770/14882773 though it still has an Access violation error at least it has output as expected

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
    
struct pelajar {
    string NIM;
    int num;
    };

int main()
{
    fstream data;
    pelajar lopzx = {"123",1};
    data.open("data.bin", ios::binary | ios::in | ios::out);

    pelajar man;
    data.write(reinterpret_cast<char*>(&lopzx), sizeof(pelajar));
    data.close();                                                           //close data
    data.open("data.bin", ios::binary | ios::in | ios::out);                //re-open data
    data.read(reinterpret_cast<char*>(&man), sizeof(pelajar));              //then read
    std::cout << man.NIM << man.num;

    data.close();
}

But if we change struct NIM to a char array, close, then reopen there will be no error anymore

pelajar {
    char NIM[50];
    int num;
};

and give it a value like this:

pelajar lopzx = {"123",1};

Note : Somehow outside of lopzx curly braces lopzx.NIM = "val"; doesn't work. and there are probably cons to this method that I'm not aware of.