Search code examples
cstructmallocfwritefread

fwrite and fread with malloced nested struct does not have expected behaviour


I have an array of nested structs pointers allocated with malloc. I want print the array in a binary file and then, load it into this struct again crushing existing records. This is my structs array:

struct viaje {
    char *identificador;
    char *ciudadDestino;
    char *hotel;
    int numeroNoches;
    char *tipoTransporte;
    float precioAlojamiento;
    float precioDesplazamiento;
};

struct cliente {
    char *dni;
    char *nombre;
    char *apellidos;
    char *direccion;
    int totalViajes;
    struct viaje *viajes;
} *clientes;

I am trying to print it inside a binary file like:

for (i = 0; i < MAX_TAM_CLIENTES; i++) {
    fwrite(&clientes[i], sizeof(struct cliente)-(sizeof(struct viaje)*MAX_TAM_VIAJES_CLIENTE), 1, fp_guardarCargarEstado);

    for (j = 0; j < clientes[i].totalViajes; j++) {
        fwrite(&clientes[i].viajes[j], sizeof(struct viaje), 1, fp_guardarCargarEstado);
    }
}

Where:

MAX_TAM_CLIENTES is a define. Is the max size of array clientes,
MAX_TAM_VIAJES_CLIENTE is a define. Is the max size of array viajes inside one clientes

Also, I try to load this binary data like:

    for (i = 0; i < MAX_TAM_CLIENTES; i++) {
        clientes = (struct cliente *)realloc(clientes, (totalClientes+1)*sizeof(struct cliente));
        clientes[totalClientes].dni = (char *)malloc((MAX_TAM_DNI+1)*sizeof(char));
        clientes[totalClientes].nombre = (char *)malloc((MAX_TAM_NOMBRE+1)*sizeof(char));
        clientes[totalClientes].apellidos = (char *)malloc((MAX_TAM_APELLIDOS+1)*sizeof(char));
        clientes[totalClientes].direccion = (char *)malloc((MAX_TAM_DIRECCION+1)*sizeof(char));

        fread(&clientes[i], sizeof(struct cliente)-(sizeof(struct viaje)*MAX_TAM_VIAJES_CLIENTE), 1, fp_guardarCargarEstado);

        for (j = 0; j < clientes[i].totalViajes; j++) {
            clientes[i].viajes = (struct viaje *)realloc(clientes[i].viajes, (j+1)*sizeof(struct viaje));
            clientes[i].viajes[j].identificador = (char *)malloc((MAX_TAM_IDENTIFICADOR+1)*sizeof(char));
            clientes[i].viajes[j].ciudadDestino = (char *)malloc((MAX_TAM_CIUDAD_DESTINO+1)*sizeof(char));
            clientes[i].viajes[j].hotel = (char *)malloc((MAX_TAM_HOTEL+1)*sizeof(char));
            clientes[i].viajes[j].tipoTransporte = (char *)malloc((MAX_TAM_TIPO_TRANSPORTE+1)*sizeof(char));

            fread(&clientes[i].viajes[j], sizeof(struct viaje), 1, fp_guardarCargarEstado);
        }
    }

Where:

MAX_TAM_* are defines. They are the maximum size of each field.

But I do not know if fwrite is saving good the data due to when I try load it, program crash and is not loading data.

I do not know how could be wrong there. Any idea?

Thank you.


Solution

  • You cannot save the full contents of these structures with fwrite and read them back with fread, because they contain pointers to strings, not the actual characters.

    There are 2 ways to achieve your goals:

    • save the contents as text in a standard format such as XML or simply CSV where each entry is written on a single line with the different fields are separated by ,.
    • change the structures to store the strings as arrays of char instead of pointers to allocated storage. If you open the files as binary, you should be able to write the structures and read them back withfread`.

    Be aware however that the latter approach is much less flexible than the textual approach:

    • if you change the structure, you can no longer read previously saved contents with the new version of the program.
    • the binary format is potentially not portable to another architecture, such as a smartphone, because the representation of int may be different.
    • the member struct viaje *viajes; must also be replaced with an actual structure without any pointers.