2011-12-31 16 views
6

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.

Respuesta

9

Está escribiendo los datos en la estructura, que es un int seguido de un puntero a una cadena. Solo se trata de datos como cualquier otra cosa, y sabes cuánto tiempo es porque la estructura es de longitud fija, un int más un puntero. Lees el mismo puntero al mismo nombre que el original. El nombre en sí no está escrito ni leído.

+0

+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

+0

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. –

1

El puntero * name sigue siendo válido en todas las llamadas fwrite y fread, lo que es aparentemente una casualidad para usted. Si libera (persona-> nombre) antes de imprimir, obtendrá el resultado o error que esperaba.

5

Ambos person->name y person_read->name terminan señalando la misma ubicación de memoria. Como no desasignó person->name antes de volver a leer el archivo, el valor del puntero en person_read->name sigue siendo válido.

Si ha desasignado person->name o leyó el archivo de un programa diferente, el valor del puntero dejaría de ser válido, e intentar hacer referencia a él invocaría un comportamiento indefinido; habría impreso un galimatías o habría obtenido una segfault.

+0

+1 Gracias, esto tiene sentido después de leer su respuesta. – tsliwkan

Cuestiones relacionadas