81

Let's say I have this struct

typedef struct person{
    char firstName[100], surName[51]
} PERSON;

and I am allocating space by malloc and filling it with some values

PERSON *testPerson = (PERSON*) malloc(sizeof(PERSON));
strcpy(testPerson->firstName, "Jack");
strcpy(testPerson->surName, "Daniels");

What is the correct and safe way to free all memory taken by that struct? Is "free(testPerson);" enough or do I need to free each struct's attribute one by one?

It leads me to another question - how are structures stored in memory? I noticed a strange behaviour - when I try to print structure address it's equal to it's first attribute's address.

printf("Structure address %d == firstName address %d", testPerson, testPerson->firstName);

Which means that this free(testPerson) should be equal to this free(testPerson->firstName);

and that's not what I want to do.

Thanks

user10099
  • 1,345
  • 2
  • 17
  • 23
  • 47
    A good rule of thumb: for every malloc you need exactly one free (no more, no less). – Chris Eberle Nov 27 '12 at 18:40
  • sometimes you may want to free early and this may need more free's (due to some being in conditional if statements) than malloc's – Coder Jul 13 '22 at 17:51

8 Answers8

99

Simple answer : free(testPerson) is enough .

Remember you can use free() only when you have allocated memory using malloc, calloc or realloc.

In your case you have only malloced memory for testPerson so freeing that is sufficient.

If you have used char * firstname , *last surName then in that case to store name you must have allocated the memory and that's why you had to free each member individually.

Here is also a point it should be in the reverse order; that means, the memory allocated for elements is done later so free() it first then free the pointer to object.

Freeing each element you can see the demo shown below:

typedef struct Person
{
char * firstname , *last surName;
}Person;
Person *ptrobj =malloc(sizeof(Person)); // memory allocation for struct
ptrobj->firstname = malloc(n); // memory allocation for firstname
ptrobj->surName = malloc(m); // memory allocation for surName

.
. // do whatever you want

free(ptrobj->surName);
free(ptrobj->firstname);
free(ptrobj);

The reason behind this is, if you free the ptrobj first, then there will be memory leaked which is the memory allocated by firstname and suName pointers.

Anderson Green
  • 30,230
  • 67
  • 195
  • 328
Omkant
  • 9,018
  • 8
  • 39
  • 59
  • For the last sentence, memory leak is probably because of structure padding. – Shash Nov 28 '12 at 10:05
  • 5
    No.. I said that if `free(ptrobj)` is done then `firstname` and `suname` pointers are the members on the heap which is gone , so the memory allocated by `firstname` and `suname` will not be freed because to free it you will have to write `free(firstname)` but firstname no longer exist ..hopw you got it – Omkant Nov 28 '12 at 10:11
10

free is not enough, free just marks the memory as unused, the struct data will be there until overwriting. For safety, set the pointer to NULL after free.

Ex:

if (testPerson) {
    free(testPerson);
    testPerson = NULL;
}

struct is similar like an array, it is a block of memory. You can access to struct member via its offset. The first struct's member is placed at offset 0 so the address of first struct's member is same as the address of struct.

S Dao
  • 555
  • 4
  • 7
7

First you should know, how much memory is allocated when you define and allocate memory in below case.

   typedef struct person{
       char firstName[100], surName[51]
  } PERSON;
  PERSON *testPerson = (PERSON*) malloc(sizeof(PERSON));

1) The sizeof(PERSON) now returns 151 bytes (Doesn't include padding)

2) The memory of 151 bytes is allocated in heap.

3) To free, call free(testPerson).

but If you declare your structure as

  typedef struct person{
      char *firstName, *surName;
  } PERSON;
  PERSON *testPerson = (PERSON*) malloc(sizeof(PERSON));

then

1) The sizeof(PERSON) now returns 8 bytes (Doesn't include padding)

2) Need to allocate memory for firstName and surName by calling malloc() or calloc(). like

        testPerson->firstName = (char *)malloc(100);

3) To free, first free the members in the struct than free the struct. i.e, free(testPerson->firstName); free(testPerson->surName); free(testPerson);

Laurent Parenteau
  • 2,516
  • 20
  • 31
Viswesn
  • 4,674
  • 2
  • 28
  • 45
5

Because you defined the struct as consisting of char arrays, the two strings are the structure and freeing the struct is sufficient, nor is there a way to free the struct but keep the arrays. For that case you would want to do something like struct { char *firstName, *lastName; }, but then you need to allocate memory for the names separately and handle the question of when to free that memory.

Aside: Is there a reason you want to keep the names after the struct has been freed?

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • I dont want to keep nothing and that's the thing :) After freeing it (using free(testPerson)), I was still able to access it's surName by testPerson->surName and it returned right value, but I was not able to access it's first attribute (firstName), it gave me some random chars. That's why I was worried if free(testPerson) was enough – user10099 Nov 27 '12 at 18:49
  • Well I understand it now, I was just worried that the other struct attributes were stucked in memory. Thanks – user10099 Nov 27 '12 at 18:57
  • @user10099 `free` only alters some data in the parts of memory that the `alloc` family of functions use for bookkeeping, which means that the characters making up your strings stay in memory until they are overwritten, but one you have called `free` (1) it is formally incorrect to access that memory again (unless a later allocation reuses it) and (2) you don't have any guarantee of what is there. – dmckee --- ex-moderator kitten Nov 27 '12 at 19:34
  • @user10099 the reason of the memory still being there is because it wasn't still cleaned by the OS. The reason you can still access that info is for that reason, so actually if access a portion of memory after calling free it's called "undefined behavior" – dierre Oct 31 '20 at 16:37
  • 1
    @dierre thanks, after 8 years the memory somehow got cleaned itself – user10099 Nov 01 '20 at 21:20
  • 1
    Ops I didn’t notice how old the thread was. But I’m glad the situation fixed itself! – dierre Nov 01 '20 at 21:35
3

This way you only need to free the structure because the fields are arrays with static sizes which will be allocated as part of the structure. This is also the reason that the addresses you see match: the array is the first thing in that structure. If you declared the fields as char * you would have to manually malloc and free them as well.

MK.
  • 33,605
  • 18
  • 74
  • 111
2

Mallocs and frees need to be paired up.

malloc grabbed a chunk of memory big enough for Person.

When you free you tell malloc the piece of memory starting "here" is no longer needed, it knows how much it allocated and frees it.

Whether you call

 free(testPerson) 

or

 free(testPerson->firstName)

all that free() actually receives is an address, the same address, it can't tell which you called. Your code is much clearer if you use free(testPerson) though - it clearly matches up the with malloc.

djna
  • 54,992
  • 14
  • 74
  • 117
  • 1
    I think the `free(testPerson->firstName)` is confusing to beginners, regarding to why it works. – Niklas R Nov 27 '12 at 18:50
  • As I said clear code would use free(testPerson). However there's no avoiding the fact that the other call will actually work, that's how the memory model works. At some time you have to understand pointers. – djna Nov 27 '12 at 19:55
2

You can't free types that aren't dynamically allocated. Although arrays are syntactically similar (int* x = malloc(sizeof(int) * 4) can be used in the same way that int x[4] is), calling free(firstName) would likely cause an error for the latter.

For example, take this code:

int x;
free(&x);

free() is a function which takes in a pointer. &x is a pointer. This code may compile, even though it simply won't work.

If we pretend that all memory is allocated in the same way, x is "allocated" at the definition, "freed" at the second line, and then "freed" again after the end of the scope. You can't free the same resource twice; it'll give you an error.

This isn't even mentioning the fact that for certain reasons, you may be unable to free the memory at x without closing the program.

tl;dr: Just free the struct and you'll be fine. Don't call free on arrays; only call it on dynamically allocated memory.

Undeterminant
  • 1,210
  • 1
  • 8
  • 14
  • 2
    Also, Variables are usually not allocated on the heap, that's why `free(&x)` might already fail. – Niklas R Nov 27 '12 at 18:49
  • 1
    I'm tempted to downvote for really careless use of language but won't as to not discourage a new user from posting. However there are 2 glaring mistakes that need to be pointed out: it is not clear what you mean by "arrays are represented by pointers" and it is not clear what "valid code" means. int x; free(&x); is not valid code. – MK. Nov 27 '12 at 19:01
  • @MK.: Thank you for pointing out these mistakes; I was trying to figure out how to properly word it because I wanted to make sure that I wasn't using language which applies to C++ and not C. It's much clearer, now. – Undeterminant Nov 27 '12 at 19:16
0

The Hereafter AKA Reincarnation Issue

In your case I'd additionally set the whole allocated memory of your variable to zero. Otherwise it may happen that new memory allocations contain data of your free'd instance. Also set the variable to NULL finally, I ran into trouble not doing that in some cases.

void 
our_person_dispose(PERSON **p)   // pass your person as pointer to finally reset the actual variable to NULL
{
  if (!*p) return;               // check if p is not NULL for safety reasons
  memset(*p, 0, sizeof(PERSON)); // set allocated memory to 0
  free(*p);                      // free dereferenced pointer to variable
  *p = NULL;                     // set variable to NULL to avoid some bughunts later on
}

PERSON *anon_alc = (PERSON*) malloc(sizeof(PERSON));
strcpy(anon_alc->firstName, "Jack");
strcpy(anon_alc->surName, "Daniels");

our_person_dispose(&anon_alc);   // stay clean for good

Kind regards

domson
  • 205
  • 2
  • 8