14

I'm doing a lesson from the Learn C the Hard way online course. In the code sample below, I don't understand why the two free() calls are necessary. I thought one would only need to call free() once, because only one malloc() occurs. Could somebody clarify why we need two?

If I comment out free(who->name); then valgrind tells me that I've lost a chunk 'o memory, like so;

LEAK SUMMARY:
definitely lost: 21 bytes in 2 blocks

Here's the code:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

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

void Person_destroy(struct Person *who)
{
    assert(who != NULL);

    free(who->name); /* Why this one??! */
    free(who);
}

int main(int argc, char *argv[])
{
    // make two people structures
    struct Person *joe = Person_create(
            "Joe Alex", 32, 64, 140);

    struct Person *frank = Person_create(
            "Frank Blank", 20, 72, 180);

    // destroy them both so we clean up
    Person_destroy(joe);
    Person_destroy(frank);

    return 0;
}
Andy J
  • 1,295
  • 4
  • 20
  • 33
  • 6
    Because also strdup() allocates memory then it must be freed (see [doc](http://man7.org/linux/man-pages/man3/strdup.3.html)). – Adriano Repetti Jul 29 '14 at 08:12
  • `who->name = strdup(name);` here you allocate memory i.e. strdup() allocates. – P.P Jul 29 '14 at 08:13
  • 4
    `strdup` is a `malloc` in disguise. – AnT Jul 29 '14 at 08:14
  • 4
    Reading the documentation of a standard function is *faster* than asking on SO ! – Basile Starynkevitch Jul 29 '14 at 08:14
  • So the memory strdup() allocates isn't freed when you free(who)? – Andy J Jul 29 '14 at 08:14
  • How else is `who->name` going to be freed? Ask yourself who is going to do that automatically for you? How can the compiler know that `who->name` is the final reference to the memory. After all, if the struct isn't freed automatically, why should the string be> – David Heffernan Jul 29 '14 at 08:14
  • @Andy P: No. You need a separate `free` for every separate `malloc`. And `strdup` is a `malloc`. – AnT Jul 29 '14 at 08:14
  • No, it's not. Each free() deallocates memory allocated by one malloc. If you allocate space for a struct with pointers to many other structs then each one must be deallocated separately (same, for example, when you have an array of arrays). – Adriano Repetti Jul 29 '14 at 08:15
  • 5
    `strdup` is not a Standard C function, however it is in POSIX. (No Standard C functions return a pointer that requires freeing, other than `malloc`, `calloc`, `realloc`). – M.M Jul 29 '14 at 08:23
  • 3
    a standard function can be mentioned in some other standard than C99. Posix is such a standard. – Basile Starynkevitch Jul 29 '14 at 09:11
  • 1
    @MattMcNabb Nobody said `strdup` is a Standard *C* function. It just a C function, and a standard function ;-). Anyway, I agree it's important to remember that non-POSIX C environments often don't have it. – hyde Jul 29 '14 at 13:25

1 Answers1

24

strdup(3) is documented as

   The strdup() function returns a pointer to a new string which is a
   duplicate of the string s.  Memory for the new string is obtained
   with malloc(3), and can be freed with free(3).

BTW, as Matt McNabb commented, strdup is standard in Posix, not in the C99 language specification.

Of course free only releases the memory zone that you pass it (it does not magically and indirectly free any zone pointed inside the memory zone you pass it). Again, free(3) says:

   The free() function frees the memory space pointed to by ptr, which
   must have been returned by a previous call to malloc(), calloc() or
   realloc().  Otherwise, or if free(ptr) has already been called
   before, undefined behavior occurs.  If ptr is NULL, no operation is
   performed.

Read much more about C dynamic memory allocation. If you don't like that, learn about garbage collection. With C on Linux and some other systems, you could consider using Boehm's conservative garbage collector. You'll then use GC_MALLOC and/or GC_MALLOC_ATOMIC instead of malloc, and GC_STRDUP instead of strdup and you won't bother about free (you might sometimes use GC_FREE if wanted). I find it very useful, but it does have some drawbacks (a bit slower than malloc, and no explicit guarantee about releasing memory...).

Read about memory corruption, and memory leaks

BTW, you should first compile your program with all warnings and debug info (e.g. gcc -Wall -g). Then you could use your debugger (gdb), set a breakpoint in malloc after main has been reached, and see when malloc is called. You'll see that  strdup is calling malloc ....


FYI, on Linux, malloc is implemented using mmap(2) -and sometimes the old sbrk(2)- syscalls -to get "large" memory regions (of several kilobytes or even megabytes), and free may sometimes call munmap(2) -for these large regions- but most often it justs mark a freed block as reusable, so that block could be reused in some future calls to malloc. Hence a program doing malloc and free might not release all it previously used memory to the kernel. See also this question about memory fragmentation.

use also Valgrind and the address sanitizer

On some operating systems (e.g. Linux) you could compile your C code (using GCC) using gcc -Wall -Wextra -g then use the valgrind tool at runtime. It slows down the execution, but it is helpful in finding bugs like some memory leaks and some buffer overflow. With a recent GCC or Clang compiler, you could also use -at compile time- its address sanitizer (instrumenting the generated code).

You might also read the GC handbook, related to memory allocation and garbage collection algorithms.

Basile Starynkevitch
  • 1
  • 16
  • 251
  • 479