Article
Pointers and memory leaks in C
Avoiding the pitfalls
On this page
Introduction
Ask anybody working with C what bothers them the most about C, and many of them will probably answer Pointers and memory leaks. These are truly the items that consume most of the debugging time for developers. Pointers and memory leaks might seem to be deterrents to some programmers but, once you understand the fundamentals of pointers and associated memory operations, they will be the most powerful tool you posses in C.
This article shares the secrets that developers should know before they start programming with pointers. The article covers:
- What kinds of pointer operations cause memory corruption
- Checkpoints that must be considered while working with dynamic memory allocation
- Scenarios that result in memory leaks
If you are aware of what can go wrong beforehand, then you can take care to avoid the pitfalls and get rid of most of the pointers and memory-related problems.
What can go wrong?
Several problematic scenarios can occur that might cause trouble after your build is done. While working with pointers, you can use the information in this article to avoid many of the problems.
Uninitialized memory
In this example, p
has been allocated 10 bytes. The 10 bytes might contain garbage data, as shown in Figure 1.
Figure 1. Garbage data
![Garbage data](https://developer.ibm.com/developer/default/articles/au-toughgame/images/fig1.gif)
If a code segment tries to access this p
before a value has been assigned to it, it might get that garbage value and your program could behave mysteriously. p
might have a value that your program never expected.
A good practice is to always use memset
along with malloc
, or always use calloc
.
Now, even if the same code segment tries to access p
before a value has been assigned to it and it has proper handling of the Null
value (which ideally it should be), then it will behave properly.
Memory overwrite
Since p
has been allocated to 10 bytes, if some code snippet tries to write a value to p
that is 11 bytes, then this operation will silently, without telling you, eat up one byte from some other location. Let's assume pointer q
represents this memory.
Figure 2. Original contents of q
![Original contents of q](https://developer.ibm.com/developer/default/articles/au-toughgame/images/fig2.gif)
Figure 3. Overwritten contents of q
![Overwritten contents of q](https://developer.ibm.com/developer/default/articles/au-toughgame/images/fig3.gif)
As a result, the pointer q
will have contents that were never expected. Even if your module is coded well, it might behave incorrectly due to a coexisting module doing some memory overwriting. The example code snippet below can also explain this scenario.
In this example, the memcpy
operation is trying to write 11 bytes to p
, whereas it has been allocated only 10 bytes.
As a good practice, whenever writing values to pointers make sure to cross check the number of bytes available and number of bytes being written. Generally, the memcpy
function will be a checkpoint for this.
Memory overread
A memory overread is when the number of bytes being read are more that what they are supposed to be. This is not too serious, so I won't dwell on it. The following code gives an example.
In this example, the memcpy
operation is trying to read 20 bytes from ptr
, but it has been allocated only 10 bytes. This will also result in undesired output.
Memory leak
Memory leaks can be really annoying. The following list describes some scenarios that result in memory leaks.
- Reassignment
I'll use an example to explain reassignment.This assigns values to the memory locations shown in Figure 4 below.
Figure 4. Memory locations
memoryArea
andnewArea
have been allocated 10 bytes each and their respective contents are shown in Figure 4. If somebody executes the statement shown below (pointer reassignment )â¦then it will surely take you into tough times in the later stages of this module development. In the code statement above, the developer has assigned thememoryArea
pointer to thenewArea
pointer. As a result, the memory location to whichmemoryArea
was pointing to earlier becomes an orphan, as shown in Figure 5 below. It cannot be freed, as there is no reference to this location. This will result in a memory leak of 10 bytes.Figure 5. Memory leak
Before assigning the pointers, make sure memory locations are not becoming orphaned.
- Freeing the parent block first
Suppose there is a pointer
memoryArea
pointing to a memory location of 10 bytes. The third byte of this memory location further points to some other dynamically allocated memory location of 10 bytes, as shown in Figure 6.Figure 6. Dynamically allocated memory
IfmemoryArea
is freed by making a call to free, then as a result thenewArea
pointer also will become invalid. The memory location to whichnewArea
was pointing cannot be freed, as there is no pointer left pointing to that location. In other words, the memory location pointed bynewArea
becomes an orphan and results in memory leak. Whenever freeing the structured element, which in turn contains the pointer to dynamically allocated memory location, first traverse to the child memory location (newArea
in the example) and start freeing from there, traversing back to the parent node. The correct implementation here will be: - Improper handling of return values
At time, some functions return the reference to dynamically allocated memory. It becomes the responsibility of the
calling
function to keep track of this memory location and handle it properly.In the example above, the call to thefunc()
function inside thecallingFunc()
function is not handling the return address of the memory location. As a result, the 20 byte block allocated by thefunc()
function is lost and results in a memory leak.
Give back what you acquire
When components are developed, there can be a lot of dynamic memory allocations. You might forget to keep track of all the pointers (pointing to these memory locations), and some of the memory segments are not freed and stay allocated to the program.
Always keep track of all the memory allocations, and do free them whenever appropriate. In fact, a mechanism can be developed to keep track of these allocations, like keeping a counter in the link list node itself (but you'd have to consider the additional overhead of this mechanism as well!).
Accessing null pointer
Accessing the null pointer is very dangerous, as it might crash your program. Always make sure that you are not accessing null pointers.
Summary
This article discussed several pitfalls you can avoid when working with dynamic memory allocations. To avoid memory-related problems, it is good practice to:
- Always use
memset
along with malloc, or always usecalloc
. - Whenever writing values to pointers, make sure you cross check the number of bytes available and number of bytes being written.
- Before assigning the pointers, make sure no memory locations will become orphaned.
- Whenever freeing the structured element (which in turn contains the pointer to dynamically allocated memory location), first traverse to the child memory location and start freeing from there, traversing back to the parent node.
- Always properly handle return values of functions returning references of dynamically allocated memory.
- Have a corresponding free to every
malloc
. - Make sure you are not accessing null pointer.