Fwrite/Fread a dynamically declared struct **

0

I would like to fwrite() then fread() the following struct ** whose memory has been allocated dynamically. This struct is declared as following :

typedef struct {
   int num;
   char type;
   Entity entity;
}Cell;

I declare a map of cell as following :

typedef struct {
  char name[MAX_STRING];
  int width;
  int height;
  Cell** map;
}Maze;

I allocate the map's memory as following :

maze.map = (Cell **)malloc( width*sizeof( Cell* ));

for (int  x = 0; x < width; x++ )
{
    maze.map[x] = (Cell *)malloc(sizeof( Cell )*height);
}

The thing is is i can't fwrite()/fread() my struct like this because once I would like to fread() the struct, I would not be able to allocate the proper memory space. So i decided to write the height and width first then allocate the memory then read the map. But I can't find how to write or read my map properly. I tried :

for (int x = 0; x < maze.width; ++x) {
    for (int y = 0; y < maze.height; ++y) {
        fwrite(&maze.map[x][y], sizeof(Cell), 1, file);
    }
}

But it doesn't work, otherwise i don't know how should I write/read my map.

Everything i tried doesn't give me back the map i wrote or it just crash saying me : Process finished with exit code -1073741819 (0xC0000005)

Here are the two writing and readin fuction :

void SaveMaze(Maze maze){
   FILE *file;
   char file_name[MAX_STRING];

   strcpy(file_name, maze.name);
   strcat(file_name,".save");

   file = fopen(file_name, "w");

   if(file == NULL)
   {
       perror("Error Fopen : ");
       return ;
   }
   fwrite(maze.name,sizeof(maze.name), 1, file);
   fwrite(&maze.height,sizeof(maze.height), 1, file);
   fwrite(&maze.width,sizeof(maze.width), 1, file);

   for (int x = 0; x < maze.width; ++x) {
       fwrite(&maze.map[x], sizeof(Cell), maze.height, file);
   }

   // Close the file
   fclose(file);
}


Maze LoadMaze(char * name){
   FILE *file;
   Maze maze;
   char file_name[MAX_STRING];

   strcpy(file_name, name);
   strcat(file_name,".save");

   file = fopen(file_name, "r");

   if(file == NULL) {
       perror("Error Fopen : ");
       return maze;
   }

   fread(maze.name,sizeof(maze.name), 1, file);
   fread(&maze.height,sizeof(maze.height), 1, file);
   fread(&maze.width,sizeof(maze.width), 1, file);

   maze.map = (Cell **)malloc( maze.width*sizeof( Cell* ));

   for (int  x = 0; x < maze.width; x++ )
   {
      maze.map[x] = (Cell *)malloc(sizeof( Cell )*maze.height);
   }
   for (int x = 0; x < maze.width; ++x) {
       fread(&maze.map[x], sizeof(Cell), maze.height, file);
   }
   printf("%c",maze.map[0][0].num);

   fclose (file);
   return maze;
}
int main() {
   int choice;
   int width,height;
   char name[MAX_STRING],file_name[MAX_STRING];
   Maze maze;

   do{
       printf("1. Generate a new Maze\n");
       printf("2. Load an existing maze\n");
       printf("3. Play\n");
       printf("4. Exit\n");

       scanf("%d",&choice);
       fflush(stdin);

       if(choice == 1){
           do {
               printf("What is the width of your maze (Odd number only)\n");
               scanf("%d", &width);
               fflush(stdin);
           } while (width%2 == 0);

           do {
               printf("What is the height of your maze (Odd number only)\n");
               scanf("%d", &height);
               fflush(stdin);
           } while (height%2 == 0);

           printf("What is the name of the maze\n");
           fgets(name,sizeof(name),stdin);
           // Remove the \n from the name
           name[strcspn(name, "\n")] = 0;
           fflush(stdin);

           maze = CreateMaze(width,height,name);
           InitialyzeMaze(&maze);
           BuildMaze(&maze);
           fflush(stdin);

           SaveMaze(maze);

       }else if(choice == 2){
           //system("clear");
           //system("ls *.{save}");

           printf("What is the name of the maze you want to load ?\n");

           fgets(file_name,sizeof(file_name),stdin);
           // Remove the \n from the filename
           file_name[strcspn(file_name, "\n")] = 0;
           fflush(stdin);

           maze = LoadMaze(file_name);

       }else if(choice == 3){
           Play(&maze);
       }
   }while(choice != 4);

}

c
gcc
clion
asked on Stack Overflow Oct 7, 2019 by Xilamax • edited Oct 7, 2019 by Xilamax

2 Answers

2

You're mixing text data and binary data in your data file.

When you write the name, height, and width:

fprintf(file,"%s",maze.name);
fwrite(&maze.height,sizeof(maze.height), 1, file);
fwrite(&maze.width,sizeof(maze.width), 1, file);

This outputs a series of characters for the name (let's say "my_maze") followed immediately by sizeof(int) bytes for the height and sizeof(int) bytes for the width. So that's 7 bytes for the name, 4 bytes for height (assuming an int is 4 bytes), and 4 bytes for width.

Now when you read back:

fscanf(file,"%s",maze.name);
fread(&maze.height,sizeof(maze.height), 1, file);
fread(&maze.width,sizeof(maze.width), 1, file);

The %s format specifier to fscanf reads characters until it encounters whitespace. The first 7 characters get read in correctly but right after that is binary data for height so where does it stop reading? The result is that you most likely read more bytes than you indented to and now the rest of your reads are not in the correct place.

You can fix this by doing away with fprintf and fscanf by writing the entire name field with fwrite:

fwrite(maze.name,sizeof(maze.name), 1, file);

And reading it with fread:

fread(maze.name,sizeof(maze.name), 1, file);

You also have a problem here:

fwrite(&maze.map[x], sizeof(Cell), maze.height, file);

And here:

fread(&maze.map[x], sizeof(Cell), maze.height, file);

&maze.map[x] is not the address of the memory you allocated but the address of the pointer it points to. So rather than reading/writing the memory set aside for each row of cells you're reading/writing the memory used for the row of pointers for each cell. You end up reading/writing past the end of allocated memory when you do this.

Get rid of the address-of operator here to pass in the pointer to the actual memory you're reading/writing:

fwrite(maze.map[x], sizeof(Cell), maze.height, file);
...
fread(maze.map[x], sizeof(Cell), maze.height, file);
answered on Stack Overflow Oct 7, 2019 by dbush • edited Oct 7, 2019 by dbush
0

Since the elements in each maze item (maze.map[X]) are continuous, and are pre-allocated, you can write each 'maze' item with a single fwrite call:

for (int x = 0; x < maze.width; ++x) {
    fwrite(&maze.map[x], sizeof(Cell), maze.height, file);
}

Taking this route, you can use fread instead of fwrite to read the elements.

Side Note: Usually better to use calloc(count, sizeof(...)), instead of malloc(count*sizeof) - it will initialize the allocated memory to zeros.

answered on Stack Overflow Oct 7, 2019 by dash-o

User contributions licensed under CC BY-SA 3.0