C - Dynamic Arrays in C
A dynamic array in C is an array whose memory size is determined or changed during program execution rather than being fixed when the program is compiled. Dynamic arrays are useful when the exact number of elements is not known in advance.
For example, if a program needs to store marks for students, you may not know how many students will be entered. Instead of declaring a fixed array such as int marks[100];, you can allocate memory according to the number of students actually entered.
1. Why Dynamic Arrays Are Needed
A normal array has a fixed size:
int numbers[10];
This array can store exactly 10 integers. If you need to store 50 integers, the array is too small. If you declare:
int numbers[1000];
but only use 10 elements, a large portion of the allocated memory is unnecessary.
Dynamic arrays solve this problem by allowing memory to be allocated according to the actual requirement.
The general idea is:
Determine required size
↓
Allocate memory at runtime
↓
Store elements
↓
Use the array
↓
Resize if necessary
↓
Release memory
2. Dynamic Memory Allocation
Dynamic arrays are created using functions provided by the C standard library. These functions are declared in the stdlib.h header file.
The main functions are:
-
malloc() -
calloc() -
realloc() -
free()
They allow a program to request and manage memory during execution.
3. Creating a Dynamic Array Using malloc()
malloc() stands for memory allocation. It allocates a specified number of bytes and returns a pointer to the allocated memory.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Enter the number of elements: ");
scanf("%d", &n);
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
for (int i = 0; i < n; i++) {
printf("Enter element %d: ", i + 1);
scanf("%d", &arr[i]);
}
printf("Array elements are:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
Here, the user determines the number of elements at runtime.
If the user enters 5, this statement:
int *arr = malloc(n * sizeof(int));
allocates enough memory for five integers.
The expression:
n * sizeof(int)
calculates the total number of bytes required.
4. Understanding sizeof()
The sizeof operator determines the amount of memory occupied by a data type or variable.
For example:
sizeof(int)
returns the size of an integer on the particular system.
Instead of assuming that an integer occupies a particular number of bytes, it is better to use:
malloc(n * sizeof(int))
This makes the program more portable.
You can also write:
int *arr = malloc(n * sizeof *arr);
This avoids explicitly repeating the data type.
5. Accessing a Dynamic Array
Although dynamically allocated memory is accessed through a pointer, you can use array notation.
For example:
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
You can also access the elements using pointer arithmetic:
*(arr + 0) = 10;
*(arr + 1) = 20;
*(arr + 2) = 30;
Both approaches access the same memory locations.
The notation:
arr[i]
is equivalent to:
*(arr + i)
6. Dynamic Arrays Using calloc()
calloc() also allocates memory dynamically, but it has an important difference from malloc().
Syntax:
calloc(number_of_elements, size_of_each_element);
Example:
int *arr = calloc(n, sizeof(int));
The allocated memory is initialized to zero.
For example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Enter number of elements: ");
scanf("%d", &n);
int *arr = calloc(n, sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
The elements will initially contain zero values.
The key difference is:
malloc() → allocates memory without initializing its contents
calloc() → allocates memory and initializes the allocated bytes to zero
7. Resizing a Dynamic Array Using realloc()
One of the biggest advantages of dynamic arrays is that their allocated storage can be changed during program execution.
The realloc() function is used for this purpose.
Suppose you initially allocate space for five integers:
int *arr = malloc(5 * sizeof(int));
Later, you discover that you need space for ten integers.
You can use:
arr = realloc(arr, 10 * sizeof(int));
However, directly assigning the result to the original pointer can be risky if reallocation fails. A safer approach is:
int *temp = realloc(arr, 10 * sizeof(int));
if (temp != NULL) {
arr = temp;
}
This preserves the original pointer if realloc() cannot provide the requested memory.
8. Complete Example of Resizing an Array
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, newSize;
printf("Enter initial size: ");
scanf("%d", &n);
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
printf("Initial memory allocation failed.\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = (i + 1) * 10;
}
printf("Original array:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\nEnter new size: ");
scanf("%d", &newSize);
int *temp = realloc(arr, newSize * sizeof(int));
if (temp == NULL) {
printf("Memory reallocation failed.\n");
free(arr);
return 1;
}
arr = temp;
if (newSize > n) {
for (int i = n; i < newSize; i++) {
arr[i] = (i + 1) * 10;
}
}
printf("Resized array:\n");
for (int i = 0; i < newSize; i++) {
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
If the original array contains five elements and the size is increased to eight, realloc() attempts to provide enough storage for eight integers.
The existing elements are preserved up to the amount that remains available, while the newly added portion does not automatically receive meaningful values and should be initialized by the program when needed.
9. Reducing the Size of an Array
realloc() can also reduce the allocated size.
For example:
int *temp = realloc(arr, 3 * sizeof(int));
If the original array contained ten elements, the storage is reduced to three elements.
The elements beyond the new size should no longer be accessed.
10. Releasing Dynamic Memory
Memory allocated using malloc(), calloc(), or realloc() should eventually be released using free().
Example:
free(arr);
After calling free(), the memory is no longer available for the program to use.
A good practice is:
free(arr);
arr = NULL;
Setting the pointer to NULL helps prevent accidental use of the old address.
11. Memory Leak
A memory leak occurs when dynamically allocated memory is no longer needed but the program fails to release it.
For example:
int *arr = malloc(100 * sizeof(int));
/* program continues */
return 0;
If the allocated memory is not released with free(), the program has failed to explicitly release that allocation.
The correct approach is:
int *arr = malloc(100 * sizeof(int));
/* use arr */
free(arr);
arr = NULL;
Memory leaks can become particularly problematic in programs that run for a long time or repeatedly allocate memory.
12. Difference Between Static and Dynamic Arrays
| Feature | Static/Fixed Array | Dynamic Array |
|---|---|---|
| Size | Fixed | Determined at runtime |
| Memory management | Mostly automatic for local arrays | Programmer manages allocation |
| Resizing | Cannot directly resize | Can use realloc() |
| Flexibility | Limited | High |
| Main mechanism | Array declaration | Pointers and dynamic allocation |
| Memory release | Automatic for local arrays | Requires free() |
For example, a fixed array is declared as:
int arr[100];
A dynamic array can be created as:
int *arr = malloc(100 * sizeof(int));
Although both can provide storage for 100 integers, their memory-management models are different.
13. Dynamic Array for User Input
A practical application is accepting an unknown number of values.
For example, suppose a program initially allocates space for five numbers. When the user enters more than five numbers, the program can increase the allocated storage.
A simplified approach is:
int capacity = 5;
int size = 0;
int *arr = malloc(capacity * sizeof(int));
When the array becomes full:
if (size == capacity) {
capacity *= 2;
int *temp = realloc(arr, capacity * sizeof(int));
if (temp == NULL) {
free(arr);
return 1;
}
arr = temp;
}
The capacity is doubled each time more space is required.
This technique is commonly used when building dynamic collections.
14. Important Precautions
When working with dynamic arrays, several rules are important.
First, always check whether allocation succeeded:
if (arr == NULL) {
/* handle allocation failure */
}
Second, calculate the required allocation size correctly:
n * sizeof(int)
Third, do not access elements outside the allocated range.
If memory has been allocated for five elements:
int *arr = malloc(5 * sizeof(int));
valid indexes are:
0
1
2
3
4
Accessing:
arr[5]
is outside the allocated array and results in undefined behavior.
Fourth, use free() when the dynamically allocated memory is no longer needed.
Finally, when using realloc(), it is safer to store its result in a temporary pointer before replacing the original pointer.
15. Advantages of Dynamic Arrays
Dynamic arrays provide several important advantages.
Efficient memory usage: Memory can be allocated according to the program's actual requirements.
Runtime flexibility: The required size does not need to be known when writing the program.
Resizable storage: realloc() allows the allocated storage to be expanded or reduced.
Useful for large datasets: Programs can allocate memory based on input size rather than reserving an unnecessarily large fixed array.
Foundation for data structures: Dynamic memory allocation is an important concept for implementing structures such as dynamic lists, stacks, queues, and other data structures.
16. Limitations of Dynamic Arrays
Dynamic arrays also require careful programming.
The programmer is responsible for managing the allocated memory. Incorrect memory management can result in memory leaks, invalid memory access, dangling pointers, or program crashes.
Additionally, resizing an allocation may require moving the data to another memory location. Therefore, resizing should be handled carefully, particularly for large arrays.
17. Key Functions to Remember
The four major functions can be summarized as follows:
malloc() → Allocate memory
calloc() → Allocate and initialize memory to zero
realloc() → Resize previously allocated memory
free() → Release allocated memory
A typical dynamic-array lifecycle is:
malloc/calloc
↓
Store and access elements
↓
realloc when more/less space is needed
↓
Use the resized array
↓
free
Conclusion
Dynamic arrays allow C programs to allocate memory at runtime based on actual requirements. Unlike fixed-size arrays, they provide greater flexibility because the amount of storage can be determined during execution and can be changed later with realloc().
Understanding malloc(), calloc(), realloc(), and free() is essential for effective dynamic memory management in C. It also provides the foundation for understanding more advanced topics such as linked lists, dynamic data structures, and memory-efficient applications.