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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With