0

I have a question about an example treated in this site (define a function returning struct pointer) :

    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;

    return who;
}

The size performed by "sizeof(struct Person)" is 16 bytes for a 32 bits MCU (4 bytes for the pointer, and 3 Integers of 4 bytes). So the "malloc" function allocates 16 bytes in RAM for a new instance of the structure "Person". But in fact, when we make an affectation of "Person", the space really used for an instance of "Person" is different and can be more important, this depend of length of the "name" parameter.

Question: the divergence between the size allocated by the "malloc" function and the size really taken for an instance of "Person" could it be a problem ?

Best regards.

Community
  • 1
  • 1
  • Say what? Is your question about the memory for `Person.name`? The structure only contains a pointer to the name. If you assign a value to that pointer, it points to a separate memory block from the struct itself. – Jonathan Wood May 05 '14 at 16:08
  • What does it mean to make an affectation of a struct? (On an unrelated note, I suggest you do `struct Person *who = malloc(sizeof *who);` instead of `struct Person *who = malloc(sizeof(struct Person));`, thus more closely follwing theDRY-principle (http://en.wikipedia.org/wiki/Don't_repeat_yourself) – fstd May 05 '14 at 16:09
  • 1
    Please clarify this extremely vague sentence: "the space really used for an instance of `Person` is different and can be more important". – barak manos May 05 '14 at 16:13
  • @fstd: Huh? If `malloc` returns only enough memory for the pointer, what will `who` then point to? – Jonathan Wood May 05 '14 at 17:00
  • @JonathanWood that would be `sizeof who`, but i said `sizeof *who`. `sizeof` is an operator (not a function), which evaluates, at compile time, to the size of the type of the given expression. the type of `*who` is `struct Person`. – fstd May 05 '14 at 19:37
  • @Jonathan: Yes, but when we free "who", the string pointed by who.name is it freed also ? (application: GCC/ ARM MC3) – PascalDeLyon May 06 '14 at 13:28
  • @user3588704 I'm not Jonathan, but no. – fstd May 06 '14 at 13:45
  • @user3588704: No, they are two separate blocks of memory, allocated and freed separately. If you want them to be one block of memory, consider declaring who as `char who[135]`. – Jonathan Wood May 06 '14 at 14:17

2 Answers2

1

The strdup function makes its own call to malloc to allocate the string, so the string ends up being in an independent block of memory that needs to be freed separately. You can instead do something like:

struct Person *Person_create(char *name, int age, int height, int weight)
{
    struct Person *who = malloc(sizeof(struct Person) + strlen(name) + 1);
    assert(who != NULL);

    who->name = (char *)(who + 1);
    strcpy(who->name, name);
    who->age = age;
    who->height = height;
    who->weight = weight;

    return who;
}

which will allocate a single block with enough space for both the struct Person and the name string. This way, you need only a single free call to free the entire thing.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
1

First, the solution given by Chris Dodd is quite clever, but IMO unmaintainable and I wouldn't let such code pass the review in any project.

Second, difference in Person size and total data size is not a problem per se, but you must understand how the deallocation works.

If you call free() on a Person* you'll end up with a memory leak. free() is dumb: it will only deallocate memory block associated with Person* and it's not aware of any other associated resources that should be released (in your case: Persion::name).

I'd recommend to copy C++ constructor/destructor idom:

  1. Person_create() function is creating the object.
  2. void Person_delete(Person *p) should release name and only after than it can release Person memory.

In C++ we use operator delete that calls destructor before releasing memory region occupied by the object/structure. Destructor's job is to assure that any resources held by the object are released before we zap the memory region.

ezaquarii
  • 1,914
  • 13
  • 15
  • @ChrisDodd's amswe is not only _clever_, it is the standard way to avoid the "struct hack", when VLA's are not available. – wildplasser May 05 '14 at 19:12