I’ve been reading “Learn C the Hard Way” and I ran across an interesting problem that I was hoping someone could explain in detail. The basic exercise was to use a struct involving an example Person struct. In the first case, I had a constructor-ish method (I don’t exactly get why the tutorial did it this way if someone wouldn’t mind explaining on the side :D) along with the struct like this:
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->name = strdup(name);
who->age = age;
who->height = height;
who->weight = weight;
}
Along with rest of the code, this runs just fine, but when I print the memory location of the pointers to these structs like:
int main(int argc, char* argv[]) {
struct Person *joe = Person_create("joe alex", 12, 80, 100);
struct Person *joe = Person_create("frank blank", 20, 72, 140);
}
The difference in their memory values are always exactly 40. In comparison, an implementation of the struct constructor like:
struct Person{//is the same as above};
struct Person Person_create (char* name, int age, int height, int weight) {
struct Person newPerson;
newPerson.name = name; //and so on
return newPerson
}
When I print out the memory locations of joe and frank, initialized with the same values as above, with the above implementation, the difference in their memory location appears to be always 20.
I studied a bit of assembly and I am aware that the compiler assigns blocks of memory according to the data types of the struct so I was thinking.. in either implementations, a char array has so many chars so n characters * 1 = char space, and then I have 3 ints so 3*4 = 12; 12 + 9 (Joe Alex\0 right?) = 21.. I probably got the \0 wrong so it’s equal to 20 or something, but regardless of the specific memory size of my structs, I’m more interested in why the two different implementations result in a different size in memory (to my understanding, DOUBLE the memory cost is quite a significant amount).
In the first case 4 dynamic allocations are made: two name strings with
strdup, and 2 Person structs withmalloc. The difference between the struct addresses tells you roughly how much memory was allocated including the first struct, and before the second one[*]In the second case, no dynamic allocations are made, but I imagine you’ve created two
Personobjects on the stack:The difference between their addresses tells you roughly how much memory the first one occupies[*].
Since you haven’t copied the strings, you’re pretty much required to either use a string literal as the name, or else to manage its memory separately from the
Personstruct. The string data is not part of thestruct Person— never will be.newPerson.name = namejust stores the pointer, not the string data.This becomes a nuisance as soon as you start reading the names from a file or the terminal: you’ll have to dynamically allocate them anyway, so you won’t save any memory, but you’ll have to write more code. The exercise probably includes a
Person_Destroyfunction thatfrees the name, so that the user ofstruct Persondoesn’t have to worry about that separately.[*] Except that the implementation isn’t required to allocate these things one after the other, so your method doesn’t work in general. It just happens to be giving results on this occasion consistent with the theory that the allocations are placed in the order you made them.