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;
}
Now, strdup() uses malloc() under the hood to automatically allocate the memory for you. However, this means that you must free the memory yourself, after you finish using it! So, simply put, strdup() allocates memory using malloc() , and then copies your source string to the allocated memory.
The strdup() function allocates sufficient memory for a copy of the string str , does the copy, and returns a pointer to it.
syntax: char *strndup(const char *s, size_t n); This function is similar to strdup(), but copies at most n bytes. Note: If s is longer than n, then only n bytes are copied, and a NULL ('\0') is added at the end.
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). The strndup() function is similar, but copies at most n bytes.
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.
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.
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