Dynamic Memory Allocation in C: malloc, calloc, realloc, free with TU BCA Lab Examples
Author: Bhuban Subedi | Subject: C Programming (CACS151) | Semester: First Semester
When learning C programming in your first semester of Tribhuvan University (TU) BCA, one of the biggest bottlenecks you encounter with standard arrays is fixed sizing.
Consider this: If you declare int marks[100];, you have permanently locked away 400 bytes of memory. If your user only inputs 10 students, 360 bytes are wasted. Worse, if your user wants to enter 101 students, your program crashes with an array out-of-bounds error.
This is where Dynamic Memory Allocation (DMA) comes to the rescue. DMA allows you to request memory from the operating system’s heap at runtime, resize that memory when needed, and release it back to the OS when done.
In this tutorial, we will break down the four essential standard library functions (<stdlib.h>), compare malloc vs calloc, learn how realloc works under the hood, and write a complete TU BCA lab-ready program.
1. Static vs Dynamic Memory Allocation
Before writing code, let’s understand the architectural difference between where and how memory is allocated during execution.
+------------------------------------+------------------------------------+
| Static / Compile-Time Allocation | Dynamic / Runtime Allocation (DMA) |
+------------------------------------+------------------------------------+
| Memory is allocated on the Stack | Memory is allocated on the Heap |
| or Data Segment before execution. | dynamically during program runtime.|
+------------------------------------+------------------------------------+
| Fixed size; cannot grow or shrink | Flexible size; can be expanded or |
| during execution. | shrunk using `realloc()`. |
+------------------------------------+------------------------------------+
| Automatically deallocated when | Must be manually released using |
| the variable goes out of scope. | `free()`; else causes memory leaks.|
+------------------------------------+------------------------------------+
| Examples: `int a; int arr[50];` | Functions: `malloc`, `calloc`, |
| | `realloc`, `free` |
+------------------------------------+------------------------------------+
+-------------------------------------------------------------+
| TYPICAL PROCESS RAM LAYOUT |
| +---------------------------------------------------------+ |
| | Code / Text Segment (Compiled binary instructions) | |
| +---------------------------------------------------------+ |
| | Data Segment (Initialized & Uninitialized Global/Static)| |
| +---------------------------------------------------------+ |
| | Heap (Grows Downward -> Dynamic Memory: malloc/calloc) | |
| | | | |
| | v | |
| | ^ | |
| | | | |
| | Stack (Grows Upward <- Local variables & Function calls)| |
| +---------------------------------------------------------+ |
+-------------------------------------------------------------+
2. The Four Pillars of DMA in <stdlib.h>
All dynamic memory functions in C are declared inside the <stdlib.h> header. Let’s look at each function in detail.
1. malloc() (Memory Allocation)
malloc() allocates a single contiguous block of raw memory of the specified byte size.
- Signature:
void* malloc(size_t size); - Initial Content: Contains garbage values (whatever random bits were left in RAM).
- Return Value: Returns a generic
void*pointer to the first byte on success, orNULLif memory is exhausted.
int *ptr;
ptr = (int*) malloc(5 * sizeof(int)); // Allocates 20 bytes for 5 integers
2. calloc() (Contiguous Allocation)
calloc() allocates multiple blocks of memory of equal size and automatically initializes every single byte to zero (0).
- Signature:
void* calloc(size_t num_elements, size_t element_size); - Initial Content: All bytes are initialized to
0. - Return Value: Returns
void*pointer to the allocated memory, orNULLon failure.
int *ptr;
ptr = (int*) calloc(5, sizeof(int)); // Allocates 20 bytes and sets all to 0
3. realloc() (Re-allocation)
What if your dynamic array is full and you need more space? realloc() modifies the size of previously allocated memory without losing existing data.
- Signature:
void* realloc(void *ptr, size_t new_size); - Behavior:
- Tries to extend the existing contiguous block in place if free space follows it.
- If adjacent space is unavailable, it allocates a new larger block elsewhere on the heap, copies the old data over, frees the old block automatically, and returns the new address.
- Returns
NULLif allocation fails (while preserving the old pointer).
// Expand dynamic array from 5 integers to 10 integers
ptr = (int*) realloc(ptr, 10 * sizeof(int));
4. free() (De-allocation)
Memory allocated on the heap is not automatically cleared when a function ends. You must explicitly return it to the operating system using free().
- Signature:
void free(void *ptr); - Rule: Never pass an address that was not returned by
malloc,calloc, orrealloc.
free(ptr);
ptr = NULL; // Crucial best practice to prevent Dangling Pointer bugs!
3. Detailed Comparison: malloc() vs calloc()
This is one of the most consistent 5-mark theory questions in the Tribhuvan University BCA First Semester exam.
| Comparison Parameter | malloc() |
calloc() |
|---|---|---|
| Number of Arguments | Takes 1 argument: total_bytes |
Takes 2 arguments: (number_of_items, size_per_item) |
| Memory Initialization | Leaves memory uninitialized (contains random garbage). | Initializes all allocated bits to Zero (0). |
| Execution Speed | Faster, because it does not write zeroes over the memory. | Slightly slower due to initialization overhead. |
| Common Use Case | When you plan to overwrite all values immediately. | When you need guaranteed zero-initialized structures. |
| Example Declaration | (int*)malloc(n * sizeof(int)) |
(int*)calloc(n, sizeof(int)) |
4. Complete TU BCA Lab Program: Dynamic Array Management
Here is a clean, robust, and fully compilable C program demonstrating malloc, realloc, and free to manage dynamic student marks.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *marks = NULL;
int initialCount = 0;
int newCount = 0;
int sum = 0;
float average = 0.0;
// Step 1: Request initial capacity from user
printf("Enter initial number of students: ");
if (scanf("%d", &initialCount) != 1 || initialCount <= 0) {
printf("Invalid input! Exiting.\n");
return 1;
}
// Step 2: Allocate dynamic memory using malloc
marks = (int*) malloc(initialCount * sizeof(int));
// ALWAYS check for NULL pointer failure
if (marks == NULL) {
printf("Memory allocation failed! Out of RAM.\n");
return 1;
}
// Step 3: Input values
printf("\nEnter marks for %d students:\n", initialCount);
for (int i = 0; i < initialCount; i++) {
printf("Student [%d] Marks: ", i + 1);
scanf("%d", &marks[i]);
}
// Step 4: Demonstrate realloc to add more students
printf("\nNeed to add more students? Enter total new count (must be > %d): ", initialCount);
scanf("%d", &newCount);
if (newCount > initialCount) {
int *temp = (int*) realloc(marks, newCount * sizeof(int));
if (temp == NULL) {
printf("Reallocation failed! Keeping original array.\n");
} else {
marks = temp; // Safely update pointer to newly allocated heap block
printf("Successfully resized memory to hold %d students.\n", newCount);
// Input new additional student marks
for (int i = initialCount; i < newCount; i++) {
printf("Student [%d] Marks: ", i + 1);
scanf("%d", &marks[i]);
}
initialCount = newCount; // Update count
}
}
// Step 5: Process and calculate results
printf("\n--- FINAL CLASS REPORT ---\n");
for (int i = 0; i < initialCount; i++) {
printf("Student %d: %d\n", i + 1, marks[i]);
sum += marks[i];
}
average = (float)sum / initialCount;
printf("\nTotal Marks: %d | Class Average: %.2f\n", sum, average);
// Step 6: Free memory and prevent dangling pointers
free(marks);
marks = NULL;
printf("\nDynamic heap memory safely released.\n");
return 0;
}
5. What Is a Memory Leak & How to Prevent It?
A Memory Leak occurs when dynamically allocated memory on the heap is no longer needed by your application, but you lose the pointer reference to it without calling free().
Example of a Dangerous Memory Leak:
void leakMemory() {
int *buffer = (int*) malloc(1000 * sizeof(int)); // 4000 bytes allocated on heap
// Function finishes, local pointer 'buffer' is destroyed from stack!
// But the 4000 bytes on the heap remain occupied forever.
}
If this function is called inside a loop 10,000 times, your application will consume 40 Megabytes of unrecoverable RAM until the operating system terminates the process.
Bhuban’s Golden Rule: For every
malloc()orcalloc()you write, ensure there is a correspondingfree()before the pointer falls out of scope!
Frequently Asked Questions (FAQ)
Q1: What happens if malloc() cannot allocate the requested memory?
If the system has insufficient free RAM or contiguous heap space, malloc() returns NULL. You should always check if (ptr == NULL) before accessing any dynamic pointer to avoid Segmentation Faults (SIGSEGV).
Q2: Can we use free() on a statically declared array?
No! Calling free() on a static variable like int arr[10]; results in undefined behavior and an immediate runtime crash (free(): invalid pointer) because static arrays reside on the stack, not the dynamic heap.
Q3: What is the purpose of typecasting (int*) before malloc()?
In modern C (C99 and C11), void* is automatically promoted to any pointer type, so typecasting is technically optional. However, in Tribhuvan University exam evaluations and C++ compatibility, writing (int*)malloc(...) is strongly recommended for strict type clarity.



