๐Ÿง  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 Static/Automatic vs Dynamic Storage

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: โ€œstackโ€ and โ€œheapโ€ are useful practical models, but the exact implementation details are determined by the C implementation.

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 instead of repeating the pointed-to type. It stays correct if the pointer type changes.

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 A Safe malloc() Example

#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

Reading an allocated object before storing a valid value into it can be invalid because the allocated bytes do not automatically contain useful initial values.

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 ordinary integer arrays, this gives elements whose initial representation is all-bits-zero; for portable C teaching, use the function according to its specified byte-zero initialization rather than treating it as a universal โ€œzero every possible typeโ€ rule.

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() changes the size of a previously allocated block. The block may stay at the same address or move to a different 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 lose the original pointer if resizing fails. A temporary pointer lets you handle failure safely.

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 new size is larger, the additional part has indeterminate values.
  • On failure for a nonzero requested size, the original allocation remains valid.

17.14 free() โ€” Release the Allocation

free(ptr) releases memory previously obtained from an allocation function. After freeing the block, the pointer value should not be dereferenced.

free(p);
p = NULL;
Good habit: Setting a pointer to NULL after freeing can help prevent accidental reuse through that pointer.

17.15 What Does NULL Mean Here?

If an allocation function returns NULL, the requested allocation was not successful. A null pointer must not be dereferenced.

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). The allocation must contain enough space for every accessed element.

17.17 Dynamic Array Representation

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

The pointer stores the address of the first element. The allocation itself contains the array 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

There is more than one way to represent dynamically allocated two-dimensional data. A beginner-friendly approach is an array of row pointers, while a single contiguous block can offer simpler memory ownership and better locality.

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

Rows can be allocated separately, allowing more flexible row sizes but requiring multiple allocations and releases.

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

A robust program treats allocation failure as a possible runtime condition. It should decide what to do rather than immediately dereferencing the returned pointer.

Placement point: Interviewers often ask, โ€œWhat happens when malloc fails?โ€ The correct direction is: it can return NULL; check before dereferencing and handle the failure according to the program's requirements.

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. A leak specifically concerns dynamically allocated storage whose address has been lost without releasing the allocation.

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

A double free occurs when the same allocation is released more than once. This is invalid and can lead to serious runtime failures.

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

17.27 Use-After-Free

Use-after-free means accessing an allocation after it has been released.

free(p);
/* *p = 10;  // invalid */
Lifetime rule: A pointer value does not keep dynamically allocated storage alive. The allocation remains usable only while its lifetime is valid.

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 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.33 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.34 ๐ŸŽฏ 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 / 2000
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.35 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 โ†’