Table of Contents
KEY TAKEAWAYS
- Dynamic memory allocation (
malloc,calloc,realloc,free) manages memory at runtime - Always check the return value of
malloc()for NULL before using the allocated memory - Every
malloc()must have a matchingfree()to prevent memory leaks - Dynamic allocation is generally avoided in embedded systems due to fragmentation and determinism concerns
What is Dynamic Memory Allocation?
In C programming, memory can be allocated in two ways: statically (at compile time) and dynamically (at run time). When you declare a variable likeint x; or an array like int arr[10];, the compiler decides how much memory to reserve before the program even runs. This is called static memory allocation.But what if you don’t know how many elements you need until the program is running? That is where dynamic memory allocation comes in. It allows you to request memory from the operating system at run time and release it when you no longer need it.Dynamic memory is allocated from a region called the heap, which is different from the stack where local variables live.Stack vs Heap
Understanding the difference between stack and heap is essential before diving into dynamic allocation.| Feature | Stack | Heap |
|---|---|---|
| Allocation | Automatic (compile time) | Manual (run time) |
| Deallocation | Automatic (when function returns) | Manual (you must call free) |
| Speed | Very fast | Slower |
| Size | Limited (typically a few KB) | Much larger (depends on system) |
| Scope | Local to the function | Accessible until freed |
| Fragmentation | No | Possible |
The Four Functions for Dynamic Memory
C provides four standard library functions in<stdlib.h> for dynamic memory management:1. malloc() – Memory Allocation
malloc() allocates a block of memory of the specified size in bytes. The memory is not initialized, meaning it contains garbage values.Syntax:void *malloc(size_t size);Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5;
// Allocate memory for 5 integers
ptr = (int *)malloc(n * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Use the memory
for (int i = 0; i < n; i++) {
ptr[i] = i * 10;
}
// Print values
for (int i = 0; i < n; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// Free the memory
free(ptr);
return 0;
}Output:ptr[0] = 0 ptr[1] = 10 ptr[2] = 20 ptr[3] = 30 ptr[4] = 40
2. calloc() – Contiguous Allocation
calloc() works like malloc but takes two parameters: the number of elements and the size of each element. The key difference is that calloc initializes all bytes to zero.Syntax:void *calloc(size_t num, size_t size);Example:
int *ptr = (int *)calloc(5, sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// All values are initialized to 0
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]); // prints 0 for all
}
free(ptr);3. realloc() – Re-Allocation
realloc() changes the size of a previously allocated memory block. It can grow or shrink the block. If it grows and there is not enough contiguous space, it allocates a new block, copies the old data, and frees the old block.Syntax:void *realloc(void *ptr, size_t new_size);Example:
int *ptr = (int *)malloc(3 * sizeof(int));
ptr[0] = 10;
ptr[1] = 20;
ptr[2] = 30;
// Now we need space for 5 integers
ptr = (int *)realloc(ptr, 5 * sizeof(int));
if (ptr == NULL) {
printf("Reallocation failed!\n");
return 1;
}
ptr[3] = 40;
ptr[4] = 50;
// Old values are preserved
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr);Important: Never do ptr = realloc(ptr, new_size); in production code without a temporary pointer. If realloc fails, it returns NULL and you lose the reference to the original block, causing a memory leak. The safe pattern is:int *temp = (int *)realloc(ptr, new_size);
if (temp == NULL) {
// handle error, ptr is still valid
free(ptr);
return 1;
}
ptr = temp;4. free() – Deallocate Memory
free() releases memory that was previously allocated with malloc, calloc, or realloc. After freeing, the pointer becomes a dangling pointer and should be set to NULL.Syntax:void free(void *ptr);Best practice:
free(ptr); ptr = NULL; // Prevent dangling pointer
Common Mistakes and Pitfalls
1. Memory Leak
A memory leak occurs when allocated memory is never freed. In long-running embedded systems, this can eventually exhaust all available memory.void bad_function() {
int *ptr = (int *)malloc(100 * sizeof(int));
// ... use ptr ...
return; // Memory leaked! Never freed.
}2. Dangling Pointer
Accessing memory after it has been freed leads to undefined behavior.int *ptr = (int *)malloc(sizeof(int)); *ptr = 42; free(ptr); // ptr still holds the old address *ptr = 10; // DANGEROUS: undefined behavior
3. Double Free
Calling free on the same pointer twice causes undefined behavior and potential crashes.free(ptr); free(ptr); // DANGEROUS: double free
4. Not Checking for NULL
malloc and calloc return NULL if allocation fails. Always check the return value.Dynamic Memory in Embedded Systems
In embedded systems, dynamic memory allocation is a topic of debate. Here is why:Reasons to avoid it:- Fragmentation: Repeated allocation and freeing can fragment the heap, making large allocations impossible even when total free memory is sufficient
- Non-deterministic timing: malloc may take variable time, which is problematic in real-time systems
- Limited heap: Microcontrollers often have very limited RAM (sometimes just a few KB)
- No memory protection: Many microcontrollers lack an MMU, so a heap overflow corrupts other memory
- During initialization only (allocate once, never free)
- When the data size is truly unknown at compile time
- On larger embedded platforms (Raspberry Pi, ESP32) where RAM is more abundant
- Using a custom memory pool allocator with fixed-size blocks
A Practical Example: Dynamic Array of Sensor Readings
#include <stdio.h>
#include <stdlib.h>
typedef struct {
float temperature;
float humidity;
unsigned long timestamp;
} SensorReading;
int main() {
int capacity = 4;
int count = 0;
SensorReading *readings;
// Start with space for 4 readings
readings = (SensorReading *)malloc(capacity * sizeof(SensorReading));
if (readings == NULL) {
printf("Initial allocation failedn");
return 1;
}
// Simulate receiving sensor data
float temps[] = {23.5, 24.1, 22.8, 25.0, 24.3, 23.9};
float humids[] = {45.0, 46.2, 44.8, 47.1, 45.5, 46.0};
int num_samples = 6;
for (int i = 0; i < num_samples; i++) {
// Grow the array if needed
if (count == capacity) {
capacity *= 2;
SensorReading *temp = (SensorReading *)realloc(readings,
capacity * sizeof(SensorReading));
if (temp == NULL) {
printf("Reallocation failedn");
free(readings);
return 1;
}
readings = temp;
printf("Array grown to capacity: %d\n", capacity);
}
readings[count].temperature = temps[i];
readings[count].humidity = humids[i];
readings[count].timestamp = 1000 + (i * 5000);
count++;
}
// Print all readings
printf("\nSensor Readings:\n");
for (int i = 0; i < count; i++) {
printf(" [%lu] Temp: %.1f C, Humidity: %.1f%%\n",
readings[i].timestamp,
readings[i].temperature,
readings[i].humidity);
}
free(readings);
readings = NULL;
return 0;
}Summary
| Function | Purpose | Initializes Memory? |
|---|---|---|
| malloc() | Allocate a block of memory | No (garbage values) |
| calloc() | Allocate and zero-initialize | Yes (all zeros) |
| realloc() | Resize an existing block | Only new portion is uninitialized |
| free() | Release allocated memory | N/A |
Related on this site
- In RTOS contexts, malloc/free are usually replaced with specialised allocators — see memory management strategies for RTOS applications.
- Dynamic allocation is the source of many of the most common C bugs — leaks, double-free, use-after-free — see most common pitfalls in C.
- malloc, calloc, and realloc all return pointers; if pointers are still hazy, start with pointers in C.

Vivek Bhageria — Lead Firmware R&D Engineer, 12+ years. Ex-Bosch (automotive powertrain), MusicTribe (real-time audio), medical devices. M.Tech BITS Pilani. I write at NerdyElectronics — practical, register-level embedded systems for engineers who want to understand what’s actually happening under the hood.






