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, or NULL if 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, or NULL on 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 NULL if 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, or realloc.
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() or calloc() you write, ensure there is a corresponding free() 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.

LEAVE A REPLY

Please enter your comment!
Please enter your name here