C fread() magically reading dynamically allocated struct members, how?

喜你入骨 提交于 2019-12-10 03:52:17

问题


This is a test program that I have written for a larger project that I am working on. It has to do with writing struct data to disk with fwrite() and then reading that data back with fread(). One member of the struct is dynamically allocated.

First, here is my code

#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;
}

And the outpout

22 Name that is really, really, really, really, really, really, long.

My question is, why is this working? Shouldn't fwrite() only write the address that 'name' contains (i.e., the address of the beginning of the string)? That is, I am passing in sizeof(struct Person) to fwrite() and yet it is writing the string the 'name' is pointing to.

Even more confusing to me is the behavior of fread(). Again, if I am passing sizeof(struct Person), how is the actual value of 'name' being read? How is the memory for it being allocated?

My previous understanding of how to use fwrite() + fread() was that I would have to "manually" write the data that 'name' was pointing to, "manually" read that data, and then copy that string after allocating memory for both the structure and the 'name' member. In other words, I would have to traverse any pointers myself, write the data, and then read that data back in the same order.

EDIT: Dan and the others are correct. I have looked at the output file with xxd:

0000000: 1600 0000 0000 0000 30a0 d900 0000 0000  ........0.......

If I print out the address that 'name' contains before writing and after reading it is the same (0xd9a030), which matches the output from xxd.


回答1:


You are writing the data in the struct, which is an int followed by a pointer to a string. It's just data like anything else, and you know how long it is because the struct is fixed length - an int plus a pointer. You read the same pointer to the same name string as the original. The name itself is neither written nor read.




回答2:


Both person->name and person_read->name wind up pointing to the same memory location. Since you didn't deallocate person->name before reading the file back in, the pointer value in person_read->name is still valid.

If you had deallocated person->name or read the file from a different program, the pointer value would no longer be valid, and attempting to reference it would invoke undefined behavior - you would either have printed out gibberish or gotten a segfault.




回答3:


The *name pointer remains valid throughout the fwrite and fread calls, which is seemingly a fluke to you. If you free(person->name) before printf you would get the result or error your were expecting.



来源:https://stackoverflow.com/questions/8685903/c-fread-magically-reading-dynamically-allocated-struct-members-how

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!