CS Engineering Gyan

Dynamic Memory Allocation in C

Every array covered so far in this series has had one thing in common: its size was fixed at the moment the program was written, permanently baked into the code before it ever ran. This works fine when you already know exactly how much data your program will need to handle, but real-world programs frequently do not have that luxury. A program might need to store a list of student records where the number of students is only known once the user enters it while the program is already running.

Dynamic memory allocation solves this exact problem, allowing a program to request memory while it is actually running, based on values that are only known at that point in time, rather than being fixed permanently when the code was compiled. This memory is drawn from a region known as the heap, which is separate from the more limited space used for ordinary local variables.

In this tutorial, you will learn how the malloc, calloc, realloc, and free functions work together to manage memory dynamically, how to create arrays whose size is determined at runtime, what memory leaks and dangling pointers are, and how to avoid the most common pitfalls associated with manual memory management in C.


Why Dynamic Memory Allocation is Needed

Arrays declared with a fixed size must have that size known at compile time, before the program has even started running. If the actual amount of data needed turns out to be larger than expected, the program simply cannot accommodate it without dynamic memory allocation, since a fixed-size array cannot grow beyond its originally declared capacity.

Aspect Fixed-Size Array Dynamically Allocated Memory
Size Determined At compile time, before the program runs. At runtime, based on values calculated while the program is executing.
Memory Location Typically allocated on the stack. Allocated on the heap, a separate region of memory.
Lifespan Automatically released once the array goes out of scope. Remains allocated until explicitly released by the programmer.

Allocating Memory with malloc

The malloc function requests a block of memory of a specified size from the heap and returns a pointer to the beginning of that block. Unlike memory allocated for ordinary variables, memory obtained through malloc is not automatically initialized, meaning it may contain leftover, unpredictable values.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *scores;

    int numberOfStudents = 3;

    scores = (int *) malloc(numberOfStudents * sizeof(int));

    scores[0] = 85;

    scores[1] = 90;

    scores[2] = 78;

    printf("Second score: %d", scores[1]);

    free(scores);

    return 0;

}

Output

Second score: 90

Here, the amount of memory requested is calculated by multiplying the number of students by the size of a single integer, ensuring enough space is reserved to hold exactly the required number of values.


Allocating Memory with calloc

The calloc function is similar to malloc, but with one important difference: it automatically initializes all the allocated memory to zero. It also takes its arguments slightly differently, specifying the number of elements and the size of each element as two separate values.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *views;

    int days = 5;

    views = (int *) calloc(days, sizeof(int));

    printf("Initial value at index 2: %d", views[2]);

    free(views);

    return 0;

}

Output

Initial value at index 2: 0

Because calloc automatically sets every allocated element to zero, the value displayed here is predictable, unlike memory allocated through malloc, which may contain arbitrary leftover data until it is explicitly assigned.


Resizing Memory with realloc

The realloc function changes the size of a previously allocated block of memory, either expanding or shrinking it, while attempting to preserve the existing data already stored within it wherever possible.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *scores;

    scores = (int *) malloc(2 * sizeof(int));

    scores[0] = 85;

    scores[1] = 90;

    scores = (int *) realloc(scores, 4 * sizeof(int));

    scores[2] = 78;

    scores[3] = 92;

    printf("Third score: %d\n", scores[2]);

    printf("Fourth score: %d", scores[3]);

    free(scores);

    return 0;

}

Output

Third score: 78

Fourth score: 92

The original two values remain intact after the resize, since realloc preserves existing data while expanding the allocated block to accommodate the two additional integers.


Releasing Memory with free

Unlike ordinary local variables, which are automatically released once they go out of scope, dynamically allocated memory remains reserved until it is explicitly released using the free function. Forgetting to do this is one of the most common sources of problems in C programs that use dynamic memory allocation.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *subscriberCount;

    subscriberCount = (int *) malloc(sizeof(int));

    *subscriberCount = 25000;

    printf("Subscribers: %d", *subscriberCount);

    free(subscriberCount);

    return 0;

}

Output

Subscribers: 25000

Calling free here returns the allocated memory back to the system, making it available for other purposes once the program no longer needs it, which is an essential step for any memory obtained through malloc, calloc, or realloc.


Memory Leaks

A memory leak occurs when dynamically allocated memory is never released using free, even though the program no longer has any way to access or use it. Over time, especially in long-running programs, memory leaks can accumulate and consume increasingly large amounts of system memory.

Example

#include <stdio.h>

#include <stdlib.h>

void createLeak() {

    int *data = (int *) malloc(sizeof(int));

    *data = 100;

    // Memory is never freed here, and the pointer is lost once the function ends

}

int main() {

    createLeak();

    printf("Function completed, but memory remains allocated.");

    return 0;

}

Output

Function completed, but memory remains allocated.

Once the function finishes, the local pointer variable holding the address of the allocated memory disappears, but the memory itself remains reserved, with no remaining way for the program to reach it and release it properly.


Dangling Pointers

A dangling pointer is a pointer that continues to hold the address of memory that has already been freed. Using a dangling pointer to access or modify that memory leads to undefined behavior, since the memory it points to may have already been reassigned for a completely different purpose.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *data = (int *) malloc(sizeof(int));

    *data = 50;

    free(data);

    // data is now a dangling pointer, since the memory has already been released

    data = NULL;

    return 0;

}

Setting the pointer to NULL immediately after freeing it, as shown here, is a widely recommended practice, since it prevents the pointer from accidentally being dereferenced later while still holding the address of memory that no longer belongs to the program.


Dynamically Sized Arrays at Runtime

One of the most practical applications of dynamic memory allocation is creating an array whose size is determined by user input, something that is simply not possible with an ordinary fixed-size array declared directly in the code.

Example

#include <stdio.h>

#include <stdlib.h>

int main() {

    int numberOfVideos;

    printf("Enter number of videos to track: ");

    scanf("%d", &numberOfVideos);

    int *viewCounts = (int *) malloc(numberOfVideos * sizeof(int));

    int i;

    for (i = 0; i < numberOfVideos; i++) {

        viewCounts[i] = (i + 1) * 500;

    }

    for (i = 0; i < numberOfVideos; i++) {

        printf("Video %d: %d views\n", i + 1, viewCounts[i]);

    }

    free(viewCounts);

    return 0;

}

Output

Enter number of videos to track: 3

Video 1: 500 views

Video 2: 1000 views

Video 3: 1500 views

Here, the exact size of the array is only decided once the user enters a value while the program is already running, something a fixed-size array declared at compile time could never achieve on its own.


Comparing malloc, calloc, and realloc

Function Purpose Initializes Memory
malloc Allocates a single block of memory of a specified total size. No, memory may contain leftover, unpredictable values.
calloc Allocates memory for a specified number of elements of a given size. Yes, all allocated memory is automatically set to zero.
realloc Resizes a previously allocated block of memory, preserving existing data. Existing data is preserved; any newly added space is not guaranteed to be initialized.

Best Practices for Dynamic Memory Allocation


Common Mistakes Beginners Make

Mistake Correct Practice
Forgetting to call free after using dynamically allocated memory. Always release memory using free once it is no longer needed by the program.
Using a pointer after the memory it references has already been freed. Set the pointer to NULL immediately after freeing it, and avoid using it afterward.
Assuming malloc automatically initializes memory to zero. Use calloc instead of malloc whenever zero-initialized memory is specifically required.
Calling free more than once on the same pointer. Free a given block of memory only once, and set the pointer to NULL immediately afterward to help prevent repeated calls.

Frequently Asked Interview Questions

  1. What is dynamic memory allocation in C?
    Dynamic memory allocation allows a program to request memory while it is running, based on values known only at that time, rather than relying on a fixed size determined at compile time.
  2. What does the malloc function do?
    The malloc function allocates a block of memory of a specified size from the heap and returns a pointer to the beginning of that block.
  3. How is calloc different from malloc?
    Unlike malloc, calloc automatically initializes all allocated memory to zero, and it takes the number of elements and their size as two separate arguments.
  4. What does the realloc function do?
    The realloc function resizes a previously allocated block of memory, expanding or shrinking it while attempting to preserve the data already stored within it.
  5. Why is it important to call free on dynamically allocated memory?
    Calling free returns the allocated memory back to the system, preventing it from remaining reserved and unavailable even after the program no longer needs it.
  6. What is a memory leak?
    A memory leak occurs when dynamically allocated memory is never released using free, even though the program no longer has any way to access or use it.
  7. What is a dangling pointer?
    A dangling pointer is a pointer that continues to hold the address of memory that has already been freed, and using it afterward leads to undefined behavior.
  8. Why is it recommended to set a pointer to NULL after calling free on it?
    Setting a pointer to NULL after freeing it helps prevent the pointer from being accidentally dereferenced later while still referencing memory that no longer belongs to the program.
  9. Where is dynamically allocated memory typically stored?
    Dynamically allocated memory is drawn from a region known as the heap, which is separate from the more limited space used for ordinary local variables.
  10. What happens if malloc or calloc fails to allocate the requested memory?
    When allocation fails, these functions return NULL, which should always be checked before the returned pointer is used further in the program.
  11. Why can't a fixed-size array be resized at runtime the way dynamically allocated memory can?
    A fixed-size array's size is permanently determined at compile time, while dynamically allocated memory can be resized using realloc based on values calculated while the program is actually running.
  12. What could happen if free is called more than once on the same pointer?
    Calling free multiple times on the same pointer can lead to undefined behavior, which is why setting the pointer to NULL immediately after freeing it is considered good practice.

Summary

Dynamic memory allocation gives C programs the flexibility to work with data whose size is only known while the program is actually running, something fixed-size arrays simply cannot accommodate on their own. The malloc, calloc, and realloc functions each play a distinct role in requesting and adjusting memory from the heap, while free ensures that memory is properly released once it is no longer needed.

In this tutorial, you learned why dynamic memory allocation is necessary, how each of the core memory management functions works, how to create arrays sized according to runtime input, and how to recognize and avoid common pitfalls such as memory leaks and dangling pointers. With this understanding in place, you are ready to explore file handling, the final major topic in this series, which allows a C program to read from and write to files stored on disk.


← Previous: Structures & Unions Next: File Handling →

Home Visit Our YouTube Channel