When a C program is compiled, the compiler allocates memory to store different data elements such as constants, variables (including pointer variables), arrays and structures. This is referred to as compile-time or static memory allocation. There are several limitations in such static memory allocation:
1. This allocation is done in memory exclusively allocated to a program, which is often limited in size.
2. A static array has fixed size. We cannot increase its size to handle situations requiring more elements. As a result, we will tend to declare larger arrays than required, leading to wastage of memory. Also, when fewer array elements are required, we cannot reduce array size to save memory.
3. It is not possible (or efficient) to create advanced data structures such as linked lists, trees and graphs, which are essential in most real-life programming situations.
The C language provides a very simple solution to overcome these limitations: dynamic memory allocation in which the memory is allocated at run-time, i. e., during the execution of a program. Dynamic memory management involves the use of pointers and four standard library functions, namely, malloc, calloc, realloc and free. The first three functions are used to allocate memory, whereas the last function is used to return memory to the system (also called freeing/deallocating memory). The pointers are used to point to the blocks of memory allocated dynamically.
When called, a memory allocation function allocates a contiguous block of memory of specified size from the heap (the main memory of a computer) and returns its address. This address is stored in a pointer variable so as to access that memory block.
The memory allocated dynamically remains with the program as long as we do not explicitly return it to the system using the free function. Thus, when such memory is no longer required in our program, we should return it to the system. This is an important responsibility of a programmer, failing in which means that this memory will not be available to any program running on that machine including subsequent executions of our program. Thus, ill-designed programs (that do not free all allocated memory) will continue to eat up heap space, progressively reducing the computer’s data processing ability. This is not desirable on any computer/device. Further execution of such programs will eventually lead to system hangs due to unavailability of memory and the system must be rebooted. Such a reboot may not be feasible on computers used in critical applications, e. g., servers such as Google, Yahoo, MSN, etc. Hence, a programmer should be carefully free all the allocated memory blocks.
The heap is managed by the operating system and is allocated (on demand) to running programs. The heap is much larger than the program’s local memory used to hold program data. Thus, we can create much larger arrays and other data structures in the heap. Note that the allocation on the heap is not in a sequence, i. e., the memory blocks can be allocated anywhere. As a result, a heap memory is usually fragmented. The memory manager in the operating system decides the optimal location for allocation of a particular memory block.
Standard library functions for dynamic memory management
Recall that the C language provides four functions for dynamic memory management, namely, malloc, calloc, realloc and free. These functions are declared in stdli.b .h header file. They are summarized in Table and are described below.
Function | Typical call | Description |
malloc | malloc (sz ) | Allocate a block of size sz bytes from memory heap and return a pointer to the allocated block |
calloc | calloc in (sz) | Allocate a block of size n x sz bytes from memory heap, initialize it to zero and return a pointer to the allocated block |
realloc | realloc (bl,; sz) | Adjust the size of the memory block blk allocated on the heap to sz, copy the contents to a new location if necessary and return a pointer to the allocated block |
free | free (blk) | Free block of memory blk allocated from memory heap |
The malloc, calloc and realloc functions allocate a contiguous block of memory from heap. If memory allocation is successful, they return a pointer to allocated block (i. e., starting address of the block) as a void (i. e., a typeless) pointer; otherwise, they return a NULL pointer.
We’ll be covering the following topics in this tutorial:
The malloc function
The malloc (memory allocate) function is used to allocate a contiguous block of memory from heap. If the memory allocation is successful, it returns a pointer to the allocated block as a void pointer; otherwise, it returns a NULL pointer. The prototype of this function is given below.
void *malloc(size_t size);
The malloc function has only one argument, the size (in bytes) of the memory block to be allocated. Note that size_t is a type, also defined in stdlib.h, which is used to declare the sizes of memory objects and repeat counts. We should be careful while using the malloc function as the allocated memory block is uninitialized, i. e., it contains garbage values.
As different C implementations may have differences in data type sizes, it is a good idea to use the sizeof operator to determine the size of the desired data type and hence the size of the memory block to be allocated. Further, we should typecast the void pointer returned by these functions to a pointer of appropriate type. Thus, we can dynamically allocate a memory block to store 50 integers as shown below.
int *pa; /* pointer to the memory block to be allocated */ ...... pa= (int*) malloc(50 * sizeof(int)); /* alloc memory*/
This is often sufficient to allocate a memory block, particularly in small toy-like programs. However, in coding applications, it is a good idea to ensure, before continuing with program execution, that the memory allocation was successful, i. e., the pointer value returned is not NULL as shown below.
pa= (int*) malloc(50 * sizeof(int)); /* alloc memory*/ if (pa == NULL) { printf("Error: Out of memory ...\n''); exit(l); /*Error code 1 is used here to indicate out of memory situation */ } /* continue to use pa as an array here onwards */
Note that once memory is allocated, this dynamically allocated array can be used as a regular array. Thus, the ith element of array pa in the above example can be accessed as pa [i].
Note that we can combine the memory allocation statement with the NULL test to make the code concise (sacrificing readability) as shown below.
if ((pa= (int*) malloc(50 * sizeof(int))) == Null) { printf("Error: Out of memory ...\n"); exit(1); }
If we use this approach to create concise programs, we are likely to make mistakes at least in the beginning. Hence, observe carefully the use of parentheses in the if statement. You will soon realize that it is not as difficult as it appears to be. Since the assignment statement has lower precedence than the equality operator, the memory allocation statement is first included in a pair of parentheses and then compared with the NULL value within the parentheses of if statement as illustrated below.
The calloc function
The calloc function is similar to malloc. However, it has two parameters that specify the number of items to be allocated and the size of each item as shown below.
void *callee ( size_t n_items, size_t size ) ;
Another difference is that the calloc initializes the allocated memory block to zero. This is useful in several situations where we require arrays initialized to zero. It is also useful while allocating a pointer array to ensure that the pointers will not have garbage values.
The realloc function
The realloc (reallocate) function is used to adjust the size of a dynamically allocated memory block. If required, the block is reallocated to a new location and the existing contents are copied to it. Its prototype is given below.
void *realloc( void *block; size_t size);
Here, block points to a memory block already allocated using one of the memory allocation functions (malloc, calloc or realloc). If successful, this function returns the address of the reallocated block; or NULL otherwise.
The free function
The free function is used to deallocate a memory block allocated using one of the memory allocation functions (malloc, calloc or realloc). The deallocation actually returns that memory block to the system heap. The prototype of free function is given below.
void free (void *block);
when a dynamically allocated block is no longer required in the program, we must return its memory to the system.