Garbage collection is implemented in the majority of high-level programming languages. In C programming language, there are no signs of automatic garbage collection. Thus, programmers of this low-level language are responsible for the memory management that occurs within a program. Memory leaks are, in a way, a consequence of poor memory management.
Take a look at the 7 Q&As below to understand this and more.
A memory leak is a block of dynamically allocated memory that is not freed and that has lost all the references to it. In C programming language this occurs, when heap
memory is dynamically allocated with functions, like malloc
or calloc
but not freed (with the function free
) when this memory is no longer needed. Or when dynamically allocated memory is made inaccessible before it is freed, even though, it is still needed.
There are three common mistakes that lead to memory leaks:
[1] Improper handling of a return value.
Example 1: A memory leak because of an unhandled return value.
#
include
<stdio.h>
#
include
<stdlib.h>
char
*get_ptr
()
{
char
*ptr =
(
char
*)malloc(10);
return
ptr;
}
int
main
()
{
get_ptr
(); //pointer to heap memory is lost
return
0;
}
In the example above (Example 1), a memory leak occurs because a pointer is lost that contains the address to the dynamically allocated memory block. To fix this memory leak, we need to handle the return value of the function get_ptr
and free it when the memory is no longer necessary. Just like it is done in the example below (Example 2).
Example 2: Proper memory management of the returned value.
#
include
<stdio.h>
#
include
<stdlib.h>
char
*get_ptr
()
{
char
*ptr =
(
char
*)malloc(10);
return
ptr;
}
int
main
()
{
char
*ptr =
get_ptr
();
// ...
free
(ptr);
return
0;
}
Note: make sure to free dynamically allocated memory after you no longer need it.
[2] Another common memory management mistake is the improper freeing of an array of pointers.
Example 3: A memory leak because of improper freeing of an array of pointers.
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
()
{
char
**ptr_2D =
(
char
**)calloc(2, sizeof(
char
*));
*ptr_2D = (
char
*)calloc(5, sizeof(
char
));
free
(ptr_2D); //array of pointers
free
(*ptr_2D); //this pointer cannot be accessed anymore
return
0;
}
Here (Example 3), a memory leak is caused by the freeing of the ptr_2D
array (on line 7) because it contains elements that are not freed at that point. In this way, dynamically allocated memory (on line 6) is no longer accessible by the program. To fix this memory leak, we need to free the element within the ptr_2D
array first. Just like you can see below (example 4).
Example 4: Freeing an array with a dynamically allocated pointer in C.
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
()
{
char
**ptr_2D =
(
char
**)calloc(2, sizeof(
char
*));
*ptr_2D = (
char
*)calloc(5, sizeof(
char
));
free
(*ptr_2D); //freeing elements within the array first
free
(ptr_2D); //now the array of pointers can be freed without memory leaks
return
0;
}
[3] One more common mistake that leads to memory leaks is reassignment. When we reassign a pointer that holds an address to an allocated memory, we can get a memory leak in C programming language.
Example 5: A memory leak because of reassignment.
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
()
{
int
*ptr =
(
int
*)calloc(5, sizeof(
int
));
ptr = NULL;
free
(ptr); // memory allocated on line 5 is not accessible anymore
return
0;
}
In the example above (Example 5), a pointer ptr
is pointed to NULL
(on line 6). This causes a memory leak because the allocated memory (on line 5) cannot be freed anymore. To fix this memory leak, we need to free (example 6) or set another pointer to (example 7) the allocated memory before assigning the pointer ptr
to NULL
.
Example 6: Freeing before reassignment.
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
()
{
int
*ptr =
(
int
*)calloc(5, sizeof(
int
));
free
(ptr);
ptr = NULL;
return
0;
}
Example 7: Setting another pointer to the allocated memory address before reassignment.
#
include
<stdio.h>
#
include
<stdlib.h>
int
main
()
{
int
*ptr =
(
int
*)calloc(5, sizeof(
int
));
int
*another_ptr = ptr;
ptr = NULL;
// ...
free
(another_ptr);
return
0;
}
Note: it is safe to free a NULL pointer.
No. Memory leaks do not occur on the stack in C because stack objects have automatic storage duration. Whenever an object leaves its scope, it is automatically freed.
Memory leaks only occur because of the improper handling of those objects that have dynamic storage duration. In other words, memory leaks can only occur with heap objects.
For one, it is a great idea to avoid the most common memory management mistakes discussed in the 2nd answer. For two, it helps to follow the golden rule
, which states that for each and every malloc and calloc call, you should call free.
This applies to every path of execution. This means that if an error occurs, dynamically allocated memory should still be freed. Just like it is freed in the example below (consider line 13).
Example 8: Freeing on every path of execution (free on line 13 prevents a memory leak in case of error).
#
include
<stdio.h>
#
include
<stdlib.h>
void
take_action
()
{
int
*ptr =
(
int
*)malloc(sizeof(
int
) * 5);
if
(ptr == NULL)
{
return
;
}
int
*ptr_2 =
(
int
*)malloc(sizeof(
int
) * 5);
if
(ptr == NULL)
{
free
(ptr); //without a free call here, error above would cause a memory leak
return
;
}
// ...
free
(ptr);
free
(ptr_2);
}
int
main
()
{
take_action
();
return
0;
}
Memory leaks are never desirable, to say the least. However, they are way more dangerous and detrimental to long-running applications. Such applications, among others, include (1) servers, (2) browsers, and (3) graphic editors.
In these and other long-running applications, memory leaks have a higher probability to result in crashes, given that the number of leaks continuously grows over time.
In order to find memory leaks, you may reserve to, at least, two options. One is to use runtime detection tools like Valgrind or Dr. Memory. Two, implement a pair of counters of allocated and deallocated memory or of malloc
and free
function calls.
Example 9: Counters for free and malloc calls
static unsigned int
Allocated = 0;
static unsigned int
Deallocated = 0;
void
*Allocate_memory
(
size_t
size)
{
void
*ptr = NULL;
ptr =
malloc
(size);
if
(NULL != ptr)
{
++Allocated;
}
else
{
//Log error
}
return
(ptr);
}
void
Deallocate_memory
(
void
*ptr)
{
if
(ptr
!= NULL)
{
free
(ptr);
++Deallocated;
}
}
int
Is_all_memory_freed
()
{
return
Allocated == Deallocated;
}
Using the counter functions (example 9) helps you to keep track of all the allocations and deallocations within your program. The Is_all_memory_freed
function informs you, if all the allocated memory is freed.
Note: counter functions add overhead to a C program.
Yes. Memory leaks pose security risks to publicly accessible applications. If a malicious user can cause a memory leak by a certain sequence of actions in a publicly accessible application (think of a web browser or a router), then such an application can be negatively affected by malicious users.
To sum it all up, memory leaks are lost references to memory addresses that are not freed. These lost references, in turn, downgrade your software and make it more prone to crashes and vulnerable to malicious attacks.
To improve the quality of your programs, prevent memory leaks by following the best practices and utilizing a tracking option for memory leaks. So that your application can be memory leaks free.
Article originally posted here.