AlphaDeveloper
AlphaDeveloper

Reputation: 539

Save struct with an array of pointers into a file

I'm working on c++ and I need to save this struct into a file:

struct myStruct{
    int value;
    int value2;
    MyClass * myClass[10];
};

The way that I'm saving this struct is the following:

myStruct my_struct;

my_struct.value = 1;
my_struct.value2 = 2;
for ( int i = 0; i < 10; i++){
    my_struct.myClass[i] = new MyClass();
} 

FILE* f = fopen(path.c_str(), "wb");

if ( f != NULL){

    fwrite(&my_struct, sizeof(myStruct), 1, f);
    fclose(f);
}

But, when I want to read this file, my program crashes when try to access to the array of "MyClass":

FILE* f = fopen(path.c_str(), "rb");

        if ( f != NULL){

            fread(&my_struct2, sizeof(struct myStruct), 1, f);
            fclose(f);
        }


        for ( int i = 0; i < 10; i ++ ){

            if ( my_struct2.myClass[i] != NULL ){

                //Here is the crash
            }
        }

I've been searching but I can't find a solution. I only find topics about arrays of structs. I know that maybe I'm not searching very well.

Thanks.

Upvotes: 2

Views: 1714

Answers (3)

sameerkn
sameerkn

Reputation: 2259

Using fwrite(&my_struct, sizeof(myStruct), 1, f); is good if your struct my_struct contains purely static data(i.e the data for which memory was allocated at compile time). If it contains dynamic data(i.e the data for which memory is allocated at runtime) then you need to manually store such dynamic data. Overloading operator<< as shown my @vianney is a good method of saving/serializing dynamic data.

Upvotes: 0

vianney
vianney

Reputation: 161

I want to add some things to Sam's answer, even if I know this is not code review, you are writing C in C++.

C++ is not meant to be coded in C, it doesn't want to... It fought its entire life to break its bound with its deprecated father, to surpass him, to explore new meanings and way to solve problems and build efficient code. Don't do this to him... (I love C by the way, deprecated was a joke obviously ;) )

Here's how I'd do it:

#include <fstream>
#include <iostream>

class MyClass
{
public:
    MyClass() : _type(-1) {}
    MyClass(int type) : _type(type) {}

    inline const int    &type() const
    { return _type; }

private:
    int _type;
};

// -- overload of operator<< that permits me to write a MyClass* to a stream
std::ostream    &operator<<(std::ostream &stream, MyClass *myClass)
{
    stream << "myClass::type: " << myClass->type();
    return stream;
}

struct MyStruct
{
    int         value;
    int         value2;
    MyClass     *myClasses[10];

    MyStruct()
    {
        value = -1;
        value2 = 1;
        for (std::size_t i = 0 ; i < 10 ; ++i)
        { myClasses[i] = new MyClass(-i); }
    }
};

// -- overload of operator<< that permits me to write a MyStruct to a stream
std::ostream    &operator<<(std::ostream &stream, const MyStruct &myStruct)
{
    stream << "myStruct::"
            << "\n\t value: " << myStruct.value
            << "\n\t value2: " << myStruct.value2
            << "\n\t myClasses: ";
    for (std::size_t i = 0 ; i < 10 ; ++i)
    { stream << "\n\t\t " << myStruct.myClasses[i]; }
    return stream;
}

int main()
{
    std::ofstream outputFile("output.txt");

    if (outputFile.is_open() == false)
    { std::cerr << "Could not open file." << std::endl; return -1; }

    outputFile << MyStruct() << std::endl; // -- this is where the information is written into the file

    outputFile.close();
}

See simple way to write a struct, you could even get it back into the struct the same way with operator>> overload, bonus is you can use on any ostream, which means it will work with sstream, std::cout and everything!

Still this is not really c++-like as there is too much (unprotected) pointers and unchecked magical number sizes (MyClass *myClasses[10]; this is a no-no for me, because it implies this thing: for (std::size_t i = 0 ; i < 10 ; ++i), and this shit gives me shivers).

I would probably use an std::array here , but I wanted to keep MyStruct as you defined it so the example stay "close" to what you wrote. Another way would have been to use std::unique_ptr or std::shared_ptr.

This can seem as quite a bit of work or intimidating, but you may find that useful in the future. Same goes for using the std containers(array, set, vector, map, etc...), unique_ptr and shared_ptr. But I assure you it's worth giving some time to understand them and learn how to use them. It makes things simpler and safer.

What gave me shivers earlier would be written like this:

std::array<MyClass, 10> myClasses;

Loops would go like this:

for (std::size_t i = 0 ; i < myClasses.size() ; ++i)
{ myClasses[i].type(); }

for (std::array<MyClass, 10>::iterator itC = myClasses.begin() ; itC != myClasses.end() ; ++itC)
{ itC->type(); }

Or even better, a c++11 way to write a loop, that I find easier to read and write:

for (auto myClass : myClasses)
{ myClass.type(); }

Note that if you want to modify myClass inside this one you need to write auto& myClass : myClasses

Hope it helps you.

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118425

Your MyStruct contains twenty pointers to other structures.

By fwrite()ing the contents of your MyStruct to a file, you have successfully written twenty raw memory addresses of your other structures into the file, together with the other members of the MyStruct class.

Which, of course, is utterly meaningless when you try to read them back in another process. You've read back twenty raw memory addresses. Which mean nothing to a completely unrelated process. And, accessing those memory addresses, unsurprisingly, leads to a crash since those memory addresses, for all intents and purposes, are completely random values.

What your code needs to do is not write twenty raw pointer addresses to the file, but the contents of those pointers, and what they point to.

Upvotes: 1

Related Questions