๐Ÿง  17. Dynamic Memory Allocation

17.1 What Is Dynamic Memory Allocation?

Dynamic memory allocation lets a C program request storage while the program is running. This is useful when the required amount of memory is not known when the source code is written.

Fixed-size storage

Use ordinary variables and fixed-size arrays when the required storage is known in advance.

Dynamic storage

Request storage at runtime when the program discovers how much data it needs.

Runtime size โ†’ request memory โ†’ check result โ†’ use it โ†’ resize if needed โ†’ free it

17.2 Why Do We Need Dynamic Memory?

  • The number of elements may be known only after input is read.
  • A program may need storage that grows or shrinks during execution.
  • Large or variable data structures can be created only when required.
  • Dynamic storage is fundamental to linked lists, trees, dynamic arrays and many real programs.
Think like a programmer: If the user enters n, a fixed array such as int a[1000] may reserve more space than necessary. Dynamic allocation can request storage for exactly the required number of elements.

17.3 Automatic Arrays vs Dynamic Allocation

Ordinary / Automatic StorageDynamic Storage
Size is usually determined by the declaration.Size is requested during execution.
Lifetime is managed automatically for automatic objects.Programmer controls the allocation and release.
Example: int a[10];Example: int *a = malloc(10 * sizeof *a);
No free() is required for an ordinary automatic array.The allocated block must eventually be released with free().

17.4 Stack and Heap โ€” A Practical Mental Model

Automatic local objects are commonly associated with stack storage, while memory returned by malloc(), calloc() and realloc() comes from dynamically allocated storage, commonly called the heap.

Automatic/local objects
โ†“
Stack-oriented storage
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Dynamic allocations
โ†“
Heap / dynamic storage

Important: These names describe a common implementation. The C rules that matter here are when an object begins to exist, when it stops existing, and who is responsible for releasing allocated storage.

17.5 malloc() โ€” Request a Block of Bytes

malloc() allocates a block of memory containing the requested number of bytes. Its contents are indeterminate; the function does not initialize the allocated bytes for you.

#include <stdlib.h>

int *p = malloc(5 * sizeof *p);
Best habit: Prefer sizeof *p when calculating the size of one pointed-to object. It keeps the expression tied to the pointerโ€™s declared type. Also check the element count and any multiplication used to form the byte count.

17.6 The malloc() Lifecycle

1. Declare pointer
int *p;
โ†“
2. Allocate
p = malloc(n * sizeof *p);
โ†“
3. Check
if (p == NULL)
โ†“
4. Use
p[i]
โ†“
5. Release
free(p);

17.7 Basic malloc() Example with a Result Check

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int n;
    scanf("%d", &n);

    int *a = malloc((size_t)n * sizeof *a);
    if (a == NULL)
        return 1;

    for (int i = 0; i < n; i++)
        a[i] = i + 1;

    for (int i = 0; i < n; i++)
        printf("%d ", a[i]);

    free(a);
    return 0;
}

17.8 malloc() Does Not Initialize Your Data

This example assumes that n was read successfully, is positive, and gives a byte count that fits size_t. A program accepting unrestricted input must check those conditions before allocating. After allocation, assign each element before reading it; malloc does not initialize the data. Section 17.32 shows a size-overflow check.

Do not assume: malloc() means โ€œgive me an array filled with zero.โ€ It means โ€œgive me a block of at least this many bytes.โ€

17.9 calloc() โ€” Allocate and Zero-Initialize

calloc(count, size) allocates space for an array of count objects, each of size bytes, and initializes the allocated bytes to zero.

int *a = calloc(5, sizeof *a);

For an int array, zeroed bytes give initial values of 0. This is a byte-initialization rule: do not assume that all-bits-zero is the correct representation of every pointer or floating-point value on every C implementation.

17.10 malloc() vs calloc()

FunctionArgumentsInitializationTypical use
malloc(bytes)Total bytesAllocated bytes are not initializedWhen you will assign values yourself
calloc(count, size)Number ร— sizeAllocated bytes are initialized to zeroWhen zero-initialized storage is useful

17.11 realloc() โ€” Resize an Existing Allocation

realloc() requests a new size for an existing allocation. On success, use the returned pointer for the resized block. Stop using old pointer copies, including pointers to elements in the old block, even if the allocation appears to remain at the same address.

int *tmp = realloc(a, new_count * sizeof *a);
Key idea: Never assume the address stays the same after realloc().

17.12 Why a Temporary Pointer Matters

int *tmp = realloc(a, new_count * sizeof *a);

if (tmp != NULL) {
    a = tmp;
} else {
    /* a is still valid here */
}

Assigning realloc() directly to the only pointer can overwrite it with NULL on failure. For a nonzero requested size, keep the original pointer until you know the resize succeeded. Only then assign the temporary result to it.

17.13 realloc() โ€” What Can Change?

  • The allocation may grow in place.
  • The allocation may move to a new location.
  • Existing contents are preserved up to the smaller of the old and new sizes.
  • When the block grows, assign values to the added elements before reading them. realloc does not zero that new part.
  • If a nonzero resize request fails, the original block remains allocated. Decide whether to continue with it or release it. Avoid using realloc(p, 0) as a shortcut for free: zero-size behavior differs across C versions and implementations.

17.14 free() โ€” Release the Allocation

free(ptr) ends the lifetime of the allocated block. Release a live allocation using its starting pointer, not an interior pointer such as p + 1. Do not free an automatic array. free(NULL) is allowed and does nothing.

free(p);
p = NULL;
Good habit: Setting a pointer to NULL after free helps prevent reuse through that variable. It does not update other pointers that referred to the same block; those copies must not be used either.

17.15 What Does NULL Mean Here?

If an allocation function returns NULL for a positive-size request, no usable block was returned. Handle that result before indexing or dereferencing the pointer. Keep zero-element requests separate from this normal allocation path.

int *p = malloc(n * sizeof *p);

if (p == NULL) {
    printf("Allocation failed\n");
    return 1;
}

17.16 Dynamic Arrays

A pointer returned by an allocation function can be indexed using normal array notation.

int *a = malloc(n * sizeof *a);

if (a != NULL) {
    a[0] = 10;
    a[1] = 20;
}

free(a);

The expression a[i] is equivalent to *(a + i). If n elements were allocated, valid indexes are 0 through n - 1. The pointer itself does not store n, so keep the count separately.

17.17 Dynamic Array Representation

int *a โ†’ โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 10 โ”‚ 20 โ”‚ 30 โ”‚ 40 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”˜
0 1 2 3

The pointer variable and allocated block are different objects. Assigning the pointer to another pointer copies the address; it does not duplicate the elements.

17.18 Dynamically Allocated Strings

Strings are character arrays ending in '\0'. Dynamic allocation therefore needs room for the terminator.

const char *source = "CodeBhavya";
size_t n = strlen(source) + 1;
char *copy = malloc(n);

if (copy != NULL) {
    strcpy(copy, source);
    printf("%s\n", copy);
    free(copy);
}

When using string functions, include <string.h> and make sure the destination has enough capacity.

17.19 Dynamic Structures

struct Student {
    int roll;
    float marks;
};

struct Student *s = malloc(sizeof *s);

if (s != NULL) {
    s->roll = 101;
    s->marks = 86.5f;
    free(s);
}

Use -> when accessing a structure through a pointer.

17.20 Dynamic Array of Structures

struct Student *students =
    malloc(n * sizeof *students);

if (students != NULL) {
    students[0].roll = 101;
    students[0].marks = 86.5f;
}

free(students);

This pattern is useful for runtime-sized collections of records.

17.21 Dynamic 2D Data โ€” Choose a Representation

Choose how the rows will be stored. One contiguous block needs one release and uses an index such as r * cols + c. Separate row allocations need a row-pointer array and a cleanup step for every allocated row.

int *matrix = malloc(rows * cols * sizeof *matrix);

if (matrix != NULL) {
    matrix[r * cols + c] = 42;
}

free(matrix);

Contiguous block

One allocation; access with r * cols + c.

Array of pointers

Each row can have its own length. If allocation fails partway through, release the rows already allocated before releasing the pointer array.

17.22 Allocation Size โ€” Use sizeof Correctly

int *a = malloc(n * sizeof *a);
double *b = malloc(n * sizeof *b);
struct Student *s = malloc(n * sizeof *s);

This avoids hard-coding type sizes. For byte-count calculations involving an object count, use an appropriate unsigned size type such as size_t.

17.23 Allocation Failure Is Also a Program State

Allocation can fail even when the calculation is valid. Check the result, then decide how the program should respond. Do not use a failed result as an array.

Placement point: Interviewers often ask, โ€œWhat happens when malloc fails?โ€ The correct direction is: it can return NULL; report the failure, release any other storage already owned by this task, and return or choose another valid action.

17.24 Memory Leak

A memory leak occurs when dynamically allocated memory is no longer reachable by the program and therefore cannot be released.

int *p = malloc(100 * sizeof *p);

/* if p is overwritten or lost without free(p),
   the allocation can leak */
Leak โ‰  unused variable. In the example, assigning NULL loses the only pointer to an allocated block without freeing it. Setting a pointer to NULL is not a substitute for free.

17.25 Dangling Pointer

A dangling pointer is a pointer that refers to storage whose lifetime has ended.

int *p = malloc(sizeof *p);
if (p != NULL) {
    *p = 25;
    free(p);
    p = NULL;
}

The important rule is not to dereference the old pointer after free().

17.26 Double Free

Releasing the same live allocation twice has undefined behavior. This can happen when two pointers share a block and each is treated as its owner. Decide which part of the program will release it.

free(p);
p = NULL;
/* Do not free the same allocation again. */

17.27 Use-After-Free

Use-after-free means trying to read or write an object after its allocation has been released. A second pointer to the block does not keep it alive.

free(p);
/* *p = 10;  // invalid */
Lifetime rule: Keep allocated storage alive until its final valid use. After releasing it, stop using all pointers that referred to that storage.

17.28 The Complete Dynamic Memory Workflow

Need runtime storage?
โ†“
Calculate size safely
n * sizeof *p
โ†“
Allocate
malloc / calloc
โ†“
Check result
p != NULL
โ†“
Initialize and use
โ†“
Need more/less space?
realloc with a temporary pointer
โ†“
Finish
free(p)

17.29 malloc(), calloc(), realloc(), free() โ€” Quick Comparison

FunctionPurposeImportant point
malloc()Allocate a byte blockContents are not initialized
calloc()Allocate an array of objectsAllocated bytes are zero-initialized
realloc()Resize an allocationBlock may move; use a temporary pointer
free()Release an allocationDo not access the block afterward

17.30 Common Dynamic Memory Mistakes

โŒ No NULL check

Dereferencing a failed allocation is invalid.

โŒ Wrong size

Allocate enough bytes for every object, using sizeof.

โŒ Lost pointer

Overwriting the only pointer can create a memory leak.

โŒ Unsafe realloc

Use a temporary pointer when the old allocation must be preserved on failure.

โŒ Use-after-free

Never read or write through a pointer to released storage.

โŒ Double free

Release each allocation only once.

17.31 Dynamic Memory Problem-Solving Method

  1. Determine how many objects are required.
  2. Calculate the required byte count.
  3. Allocate with malloc() or calloc().
  4. Check for NULL.
  5. Initialize before reading values.
  6. Stay within the allocated bounds.
  7. Use realloc() carefully if the size changes.
  8. Release every successful allocation exactly once.
CodeBhavya Rule: Dynamic memory is not just about malloc(). Always think in three questions: How much memory? Who owns it? When is its lifetime finished?

17.32 Allocation-Size Overflow and Robust Dynamic Arrays

Before forming count * sizeof *p, check whether count exceeds SIZE_MAX / sizeof *p. Otherwise, an oversized multiplication can wrap and request a block smaller than the program expects. A successful malloc alone does not prove the size calculation was correct.

Complete C program:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void)
{
    size_t count = 4;
    if (count > SIZE_MAX / sizeof(int)) {
        fputs("Size overflow\n", stderr);
        return 1;
    }
    int *items = malloc(count * sizeof *items);
    if (items == NULL) return 1;
    for (size_t i = 0; i < count; ++i) items[i] = (int)i * 2;
    printf("Last = %d\n", items[count - 1]);
    free(items);
    return 0;
}

Expected output:

Last = 6

Step-by-step explanation:

  1. SIZE_MAX is the largest size_t value.
  2. The division check rejects counts that would overflow the byte calculation.
  3. The successfully allocated array is initialized and freed.
Remember: For a nonzero resize, preserve the old pointer until realloc succeeds. On success, replace it with the returned pointer and stop using old aliases. If the requested element count is zero, handle release explicitly with free.

17.33 Quick Revision

๐Ÿ“Œ malloc() โ†’ Allocates memory

๐Ÿ“Œ calloc() โ†’ Allocates and zero-initializes bytes

๐Ÿ“Œ realloc() โ†’ Resizes an allocation

๐Ÿ“Œ free() โ†’ Releases memory

๐Ÿ“Œ NULL โ†’ Indicates a null pointer

๐Ÿ“Œ Heap โ†’ Commonly used for dynamic storage

๐Ÿ“Œ Memory leak โ†’ Allocated memory is not released

๐Ÿ“Œ Dangling pointer โ†’ Pointer refers to an object whose lifetime has ended

๐Ÿ“Œ Use-after-free โ†’ Accessing freed memory

๐Ÿ“Œ sizeof *ptr โ†’ Useful for type-safe allocation sizing
INTERACTIVE LEARNING

๐ŸŽฌ Dynamic Memory โ€” Allocation Lifecycle

Follow the complete lifecycle from requesting heap memory to releasing it safely.

PROGRAM TRACING

๐Ÿ”Ž Program Tracing โ€” Dynamic Memory Allocation

Trace allocation, initialization, safe resizing with a temporary pointer, and release.

17.34 Quick MCQs

Select an answer first, then click Check Answer. A correct choice becomes green. If the answer is wrong, your choice becomes red and the correct option becomes green. The explanation appears below.

1. Which function allocates dynamic memory without initializing it?
2. Which function allocates memory for an array and initializes its bytes to zero?
3. Which function changes the size of an allocated block?
4. Which function releases dynamically allocated memory?
5. What should be checked after malloc() or calloc()?
6. What is a memory leak?
7. What is a dangling pointer?
8. Why is a temporary pointer recommended with realloc()?
PRACTICE

17.35 ๐ŸŽฏ Practice Problems

Practice allocation, arrays, strings, structures, matrices, realloc(), allocation checks, and memory-leak prevention. Use ๐Ÿ’ป Solve It Yourself first, open Hint only when needed, and use Show Program after attempting the problem.

๐Ÿ“ˆ Dynamic Memory Allocation Practice Progress
Solved 0 / 20
Completed with Solution 0
Total Score 0 / 400
Completion 0%
A problem counts as Solved when all test cases pass without opening the full solution. Problems completed after viewing the official solution are tracked separately.
1. Allocate One Integer

Problem 1: Allocate memory dynamically for one integer and store a value in it.

Input: One integer.

Output: Print the stored integer.

2. Allocate an Array of N Integers

Problem 2: Dynamically allocate an array of n integers and initialize it with 1 to n.

Input: One positive integer n.

Output: Print the n initialized values.

3. Sum of a Dynamically Allocated Array

Problem 3: Read n numbers into a dynamically allocated array and find their sum.

Input: n followed by n integers.

Output: Print the sum.

4. Largest Element in a Dynamic Array

Problem 4: Find the largest element in a dynamically allocated array.

Input: n greater than 0 followed by n integers.

Output: Print the largest value.

5. Smallest Element in a Dynamic Array

Problem 5: Find the smallest element in a dynamically allocated array.

Input: n greater than 0 followed by n integers.

Output: Print the smallest value.

6. Reverse a Dynamic Array

Problem 6: Reverse a dynamically allocated array.

Input: n followed by n integers.

Output: Print the reversed array.

7. calloc() Initial Values

Problem 7: Use calloc() to create an array of integers and display its initial values.

Input: One positive integer n.

Output: Print n zero values separated by spaces.

8. Increase Array Size with realloc()

Problem 8: Use realloc() to increase the size of an integer array.

Input: Initial size n, n integers, new size m where m >= n, followed by m-n additional integers.

Output: Print all m values.

9. Reduce Array Size with realloc()

Problem 9: Use realloc() to reduce the size of an array.

Input: Initial size n, n integers, then new size m where 0 < m <= n.

Output: Print the first m values retained in the resized block.

10. Dynamically Allocate a String

Problem 10: Dynamically allocate memory for a string and store a user-provided string.

Input: One line of text.

Output: Print the dynamically stored string.

11. Dynamically Allocate a Structure

Problem 11: Dynamically allocate a structure and access its members.

Input: Student roll number and marks.

Output: Print the stored members.

12. Dynamic Array of Structures

Problem 12: Dynamically allocate an array of structures.

Input: n followed by n student records: roll and marks.

Output: Print all student records.

13. Dynamic 2D Matrix

Problem 13: Create a dynamically allocated 2D matrix.

Input: Rows r, columns c, followed by r*c integers.

Output: Print the matrix.

14. Add Two Dynamic Matrices

Problem 14: Add two dynamically allocated matrices.

Input: Rows r, columns c, first matrix values, then second matrix values.

Output: Print the sum matrix.

15. Transpose a Dynamic Matrix

Problem 15: Find the transpose of a dynamically allocated matrix.

Input: Rows r, columns c, followed by r*c integers.

Output: Print the transposed matrix with c rows and r columns.

16. malloc() vs calloc()

Problem 16: Demonstrate the difference between malloc() and calloc() without reading uninitialized malloc memory.

Input: One positive integer n.

Output: Print "malloc needs initialization" and whether all calloc values begin as zero.

17. Safely Handle Allocation Failure

Problem 17: Write a program that validates the requested array size and checks malloc() before use.

Input: One integer n.

Output: Print "Allocated" for an accepted request or "Request Rejected" for an unsafe request.

18. Safe realloc() with a Temporary Pointer

Problem 18: Demonstrate realloc() using a temporary pointer so the original allocation is preserved if resizing fails.

Input: Initial size n, n integers, and new size m where m >= n, followed by new values.

Output: Print the resized array.

19. Memory Leak and Prevention

Problem 19: Demonstrate how a memory leak could occur and show the correct prevention pattern.

Input: One integer.

Output: Print the stored value and "Memory Released".

20. Dynamic Student Record Management

Problem 20: Create a dynamic Student Record Management System using an array of structures.

Input: n student records (roll, one-word name, marks), then a target roll number.

Output: Print all records, then print the matching student's name and marks or Not Found.

17.36 Key Takeaway

๐ŸŽฏ Remember:

malloc() โ†’ Allocate memory

calloc() โ†’ Allocate + zero-initialize bytes

realloc() โ†’ Resize allocation

free() โ†’ Release memory

NULL โ†’ Check allocation failure

Heap โ†’ Common area for dynamic storage

Memory Leak โ†’ Allocated memory not released

Dangling Pointer โ†’ Pointer to expired/freed object

Use-After-Free โ†’ Accessing freed memory
INTERVIEW PREPARATION

๐ŸŽค Dynamic Memory Allocation โ€” Interview Questions

1. What is the difference between malloc() and calloc()?
2. Why should allocation results be checked against NULL?
3. Why is using a temporary pointer with realloc() safer?
4. What is a memory leak?
5. What is a dangling pointer?
6. What is a double free?
7. Does free() set the pointer variable to NULL automatically?
8. Why is sizeof *ptr often preferred in allocation expressions?
PLACEMENT TIPS

๐Ÿ’ก Dynamic Memory Allocation โ€” Placement Tips

  • After every important allocation, check whether the returned pointer is NULL before dereferencing it.
  • Prefer allocation expressions such as malloc(n * sizeof *arr) so the size stays tied to the pointed-to type.
  • With realloc(), use a temporary pointer and update the original pointer only after success.
  • Match every successful dynamic allocation with a clear ownership plan for exactly one eventual free().
  • After free(), do not read or write through that pointer. Setting an owning pointer to NULL can reduce accidental reuse.
  • In matrix and multi-allocation problems, if a later allocation fails, free every earlier successful allocation before returning.
EXTRA PRACTICE

โœ๏ธ Dynamic Memory Allocation โ€” Extra Practice Questions

  1. Dynamically allocate an integer array and remove all duplicate values into a new resized array.
  2. Create a dynamic string array where each student name receives only the memory it needs.
  3. Allocate a jagged 2D array where each row has a different number of columns.
  4. Build a dynamic matrix multiplication program with complete allocation-failure cleanup.
  5. Implement a growing integer list that doubles its capacity with realloc() when full.
  6. Create a dynamic structure containing its own dynamically allocated string member and write a cleanup function for it.
โ† Previous Topic: File Handling Next Topic: Command Line Arguments โ†’