Este es un programa de prueba que he escrito para un proyecto más grande en el que estoy trabajando. Tiene que ver con escribir datos estructurales en el disco con fwrite() y luego leer esos datos con fread(). Un miembro de la estructura se asigna dinámicamente.C fread() leyendo mágicamente miembros de estructura asignados dinámicamente, ¿cómo?
primer lugar, aquí es mi código
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRING_LEN 128
struct Person {
int age;
char *name;
};
int main(int argc, const char *argv[])
{
struct Person *person = calloc(1, sizeof(struct Person));
person->age = 22;
person->name = calloc(STRING_LEN, sizeof(char));
char *name = "Name that is really, really, really, really, really, really, long.";
strncpy(person->name, name, STRING_LEN);
FILE *out_file = fopen("rw.out", "w");
fwrite(person, sizeof(struct Person), 1, out_file);
fclose(out_file);
FILE *in_file = fopen("rw.out", "r");
struct Person *person_read = calloc(1, sizeof(struct Person));
fread(person_read, sizeof(struct Person), 1, in_file);
fclose(in_file);
printf("%d %s\n", person_read->age, person_read->name);
free(person->name);
free(person);
free(person_read);
return 0;
}
Y el outpout
22 Name that is really, really, really, really, really, really, long.
Mi pregunta es, ¿por qué es este trabajo? ¿No debería fwrite() escribir solo la dirección que contiene 'nombre' (es decir, la dirección del comienzo de la cadena)? Es decir, paso en sizeof (struct Person) a fwrite() y sin embargo está escribiendo la cadena a la que apunta el 'nombre'.
Aún más confuso para mí es el comportamiento de fread(). De nuevo, si estoy pasando sizeof (struct Person), ¿cómo se lee el valor real de 'name'? ¿Cómo se asigna la memoria para ello?
Mi comprensión previa de cómo usar fwrite() + fread() era que tendría que "manualmente" escribir los datos a los que apuntaba 'nombre', "manualmente" leer esos datos, y luego copiar esa cadena después de asignar memoria para la estructura y el miembro 'nombre'. En otras palabras, tendría que atravesar cualquier puntero, escribir los datos y luego leer los datos en el mismo orden.
EDIT: Dan y los otros son correctos. He mirado en el archivo de salida con xxd:
0000000: 1600 0000 0000 0000 30a0 d900 0000 0000 ........0.......
Si imprimo la dirección que 'nombre' contiene antes de escribir y después de leer lo que es lo mismo (0xd9a030), que coincide con la salida de xxd.
+1 Puede verificar esto mediante el tamaño de la plantilla de impresión (struct Person) y/o mirando dentro del archivo escrito. fread() está leyendo solo un puntero (una dirección de memoria), que coincide con el actual 'person-> name'. – leonbloy
La única garantía es que la estructura tiene la misma longitud dentro de la misma configuración de entorno. Puede cambiar el momento en que usa -m32, -m64, cambiar los parámetros de alineación global, etc. –