When working with heap allocations and pointers, it’s important to be aware of two common issues: dangling pointers and memory leaks. These issues can arise when memory is allocated on the heap and a pointer is used to access it.
Let’s take a look at a dynamic memory allocation on the heap:
int* ptr = malloc(sizeof(int)); //assume int is 4 bytes
The pointer ptr
points to a memory block of 4
bytes. There is a relationship between the pointer ptr
and the allocated memory, which we can see in the illustration below:
A dangling pointer is a pointer that no longer points to a valid memory. This usually happens when the memory has been freed, but the address is still kept inside the pointer.
Let’s consider a scenario where an integer is allocated on the heap in line 7. In lines 14–15, this integer is used, and then in line 17, the memory is freed. However, the pointer ptr
still holds the address of the (now defunct) memory block.
If we try to use ptr
again in lines 21–22, it might appear to work fine, but this is undefined behavior. On a different platform, it might cause the program to crash or produce unexpected results.
#include <stdio.h>#include <stdlib.h>int main(){//Allocate some memory on the heapint* ptr = malloc(sizeof(int));if(ptr == NULL){return 0;}//Use the memory*ptr = 5;printf("%d\n", *ptr);free(ptr);//ptr is a dangling pointer now//The below code exhibits undefined behavior*ptr = 7;printf("%d\n", *ptr);}
A good practice for avoiding this problem or making it easier to catch is to set the pointer to NULL
after freeing the memory.
free(ptr);
ptr = NULL;
If we try to use ptr
now, the program will crash, making it clear that we have an issue that needs fixing.
A memory leak happens when we do not free the allocated memory. Over time, the memory leaks add up and the system can run out of memory.
The below code generates random integers (line 15) smaller than 100
in a loop (line 12). The values are saved on the heap using ptr
allocated in line 14.
#include <stdio.h>#include <stdlib.h>#include <time.h>int main(){//Initialize the seed of the random number generatorsrand(time(NULL));int i = 0;while(i++ < 5){int* ptr = malloc(sizeof(int));*ptr = rand() % 100;printf("Generated random number %d\n", *ptr);}}
The code seems to run properly, but it has a flaw. It allocates a new memory block of 4
bytes in each iteration of the while
loop but fails to free it.
Although the loop only wastes 20
bytes (4
bytes ✕ 5 iterations) in this instance, if the loop ran infinitely and the program continued for a prolonged period, it would gradually consume all available memory.
Eventually, this would slow down the system and make it unusable unless it was restarted. The allocated and never freed memory remains unusable until the program terminates and the operating system reclaims it.
Note: By repeatedly overwriting the pointer
ptr
inside the loop, the addresses of previously allocated memory blocks are lost, making it impossible to reuse or free them later.
The solution is to free the allocated memory. In this case, we can add a call to free
inside the loop.
while(i++ < 5)
{
int* ptr = malloc(sizeof(int));
*ptr = rand() % 100;
printf("Generated random number %d\n", *ptr);
free(ptr);
}
It is worth noting that dangling pointers and memory leaks are closely related concepts. A dangling pointer situation occurs when a pointer refers to a memory that is no longer allocated, while memory leaks happen when we lose the pointer that is pointing to a memory block, making it impossible to free that block anymore.
To gain more knowledge about these concepts and to master pointers and memory, visit this course for further information and practical exercises.
Free Resources