The Heap
In the previous article, we discussed stack memory. In this article, we’ll complete the answer to the question: “Why is the stack faster than the heap?”, and also touch on why the heap is typically larger, but slower than the stack.
How to Use the Heap 🔨
A natural way to introduce heap memory is by showing a working example of how it’s used in programs.
Access 🗄️
Heap memory is a region that a program accesses by explicitly requesting a block of memory from the operating system. Unlike stack memory, data stored on the heap remains valid after a function exits.
For instance, when a function contains stack-allocated variables, those variables are automatically deallocated when the function returns. Attempting to access them afterward leads to undefined behavior, since the memory may have been overwritten.
int* stacktest() {
int arr[100] = {0};
int* ptr = &arr[0]; // Pointer to stack memory
printf("Allocated memory at: %p\n", (void*)ptr);
return ptr; // Returning pointer to stack memory (not recommended)
}
int* calloctest() {
int* arr = (int*)calloc(100, sizeof(int));
printf("Allocated memory at: %p\n", (void*)arr);
return arr;
}
int main() {
const int* sptr = stacktest();
const int* hptr = calloctest();
printf("Allocated stack memory at: %p\n", (void*)sptr);
printf("Allocated heap memory at: %p\n", (void*)hptr);
printf("Stack memory value: %d\n", *sptr); // Access stack memory
printf("Heap memory value: %d\n", *hptr); // Access heap memory
free((void*)hptr); // Free heap memory
}
Output:
Allocated memory at: 00000006C899F590
Allocated memory at: 0000011A01D49540
Allocated stack memory at: 00000006C899F590
Allocated heap memory at: 0000011A01D49540
Stack memory value: -929434080
Heap memory value: 0
stacktest
creates a zero-initialized array on the stack and returns a pointer to the array on the stack. calloctest
allocates a zero-initialized memory span on the heap and returns a pointer to the memory location.
In this case, the stack array we attempted to access contains corrupted values, not the zero we initialized it with. This is an example of stack corruption, where the memory was reused or overwritten after the function exited. This is due to the stack being a contiguous memory block which the program and functions use to perform operations they need. This is not how the stack is supposed to be used. Stack memory should not be accessed when it has left the scope, even though accessing it is still possible.
In contrast, the heap-allocated memory remains intact and returns the expected zero. This is because calloc initializes the allocated memory to zero, and the memory is allocated in an arbitrary location in the memory, making it much less susceptible to being randomly corrupted by other function calls or random memory access.
Size ↔️
The heap is also allowed to be much larger than the stack. For example:
void StackOverflow() {
// Allocate 800MB on the stack
double arr[100000000] = { 0 };
for (double i : arr)
printf("%d", i);
}
Output:
(process 56272) exited with code -1073741571 (0xc00000fd). // Stack overflow
Allocating 800MB
on the stack causes a stack overflow because it exceeds the default stack size (on Windows it's about 1MB
).
In contrast, the heap handles large allocations without crashing:
void HeapNoOverflow() {
// Allocate 800MB on the heap
double* arr = (double*)calloc(100000000, sizeof(double));
if (arr == nullptr) {
printf("Memory allocation failed!");
return;
}
for (int i = 0; i < 10; i++)
printf("%", arr[i]);
printf("Allocated memory at: %p", (void*)arr);
free(arr); // Free memory
}
Output:
Allocated memory at: 0x1e2c1fd0070
(process 53076) exited with code 0 (0x0).
Heap memory is not subject to a strict size cap like the stack, and thus 800MB can be asked from the heap without much problem. It can also be allocated from arbitrary locations in virtual memory.
How Does the Heap Work?
Prerequisites
To understand heap allocation, it helps to know a few key terms:
- 🍿Kernel: The core part of an operating system, responsible for managing CPU time, memory, and hardware interaction at the lowest level.
- 📄Page: A fixed-size memory block (typically 4096 bytes) used by the kernel to manage memory. Memory is allocated in pages rather than individual bytes for efficiency.
Allocation
Heap memory is allocated differently from the stack. The stack typically has a fixed size (e.g., 1MB on Windows), pre-allocated when a program starts. In contrast, the heap must be requested at runtime from the OS.
When you call malloc, the C standard library first tries to find space in memory pages already assigned to the process. If no space is available, it performs a system call to the kernel to request additional memory pages.
The kernel may:
- Return a pointer to the allocated memory,
- Or return MAP_FAILED to indicate allocation failure.
This address is passed back through the system layers from the kernel to the C library to malloc, and finally to the caller.
This is much more complicated than the stack, where allocation is often just a pointer subtractions and pointer arithmetics.
Summary
Why Is the Heap Slower?
The complexity of heap allocation — requiring coordination between:
- The C runtime,
- The operating system kernel,
- Bookkeeping data structures for memory blocks and pages,
…makes it significantly slower than stack allocation.
Additionally, heap memory must be manually managed with malloc and free, introducing more opportunities for bugs like memory leaks, double frees, and dangling pointers.
This means heap allocation is powerful but comes with cost:
- Slower due to system calls and metadata tracking,
- Harder to manage due to manual deallocation,
- Larger and more flexible than stack memory.
Use heap memory only when necessary, such as when:
- The data must outlive a function,
- The size is too large for the stack,
- Or the lifetime is dynamic and unpredictable.
Top comments (4)
You shouldn't mix C and C++ heap allocation. Most of your code is C, but then you use
std::println
which is C++. In C++, you should usenew
anddelete
only.Returning a pointer to stack memory is undefined behavior rendering the entire program invalid.
You're using
calloc
wrong. Instead of:it should be:
You do have a point actually I shouldn't have mixed the languages up for the sake of convenience if it might confuse the reader, it might be better to just stick with pure C.
I honestly didn't know the proper way to use calloc since I never actually saw anyone using it personally, so I just assumed it's used like malloc but with zero-initialization. You learn something new everyday do you 😊.
And yes, I do know that new and delete exist, but I specifically avoided it for the sake of clarity and ease of explanation since the topic is mostly about heap memory allocation and nothing about constructors.
Other than that, thanks for the feedback, much appreciated 🙏.
man calloc
But you ignored the more serious flaw of undefined behavior where anything is possible.
Yayayayayya another article