An Easy Guide to Understand Dynamic Memory Allocation in C Programming Language

An Easy Guide to Understand Dynamic Memory Allocation in C Programming Language

One marvelous thing about C is that it is so intertwined with memory — and by that, I mean the programmer has quite a good understanding of “what goes where". Manual memory management for dynamic memory allocation in the C programming language) is possible via a group of functions in the C standard library , namely malloc ,realloc, calloc and free as we will see later in this article.

C has three main pools of memory:

1. static: global variable storage, permanent for the entire run of the program. static int static_No;

This is the area in which the variables that have been declared global or static and those string constants (for example "My string") are stored. In this memory area, you find all those data that are present from the very beginning of a program until the end of the execution.

2. stack: local variable storage (automatic, continuous memory).

int main(void)
{
int i; /* i is only visible/usable inside main()*/
return 0;
}

A memory stack is an area in which the variables appear and disappear at a certain point during the execution of a program. We use it mainly to store the variables local to a function. These variables have a reduced scope, they are only available while the function (where we have defined them) is being executed. All these variables are stored in the stack and therefore, the area continuously receives operations to insert and delete variables.

3. heap: dynamic storage (large pool of memory, not allocated in contiguous order).

This area contains memory available to be reserved and freed at any point during the execution of a program. It is not reserved for local variable functions as in the stack, but for “dynamic memory” for data structures that are not known to be needed until the program is being executed. This is where malloc() and free() fall in.

Note that out of these three memory zones, only the global memory has a fixed size that is known when the program starts execution. Both the stack and the heap store data the size of which cannot be know until the program is executing.

Let’s talk about malloc( ) and free( )

Say that you would like to allocate a certain amount of memory during the execution of your application. You can call the malloc() function at any time, and it will request a block of memory from the heap. The operating system will reserve a block of memory for your program, and you can use it in any way you like. When you are done with the block, you return it to the operating system for recycling by calling the free() function, then other applications can reserve it later for their own use. The malloc() or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of any form.

/* Let's see the abracadabra of malloc and free in this code*/
int main()
{
    int *p; 

    p = (int *)malloc(sizeof(int)); /*requested for 4 bytes */
    if (p == 0)
    {
        printf("ERROR: Out of memory\n"); 
        return 1; 
    }
    *p = 5;
    printf("%d\n", *p);
    free(p); /*free the requested memory for int *p */
    return 0;
}

The key processes happening in the above code are:

  1. The malloc statement first looks at the amount of memory available on the heap and asks, “Is there enough memory available to allocate a block of memory of the size requested?” The amount of memory needed for the block is known from the parameter passed into malloc — in this case, sizeof(int) is 4 bytes. If there is not enough memory available, the malloc function returns the address zero to show the error (another name for zero is NULL and you will see it used throughout C code). Otherwise malloc proceeds.

  2. If memory is available on the heap, the system “allocates” or “reserves” a block from the heap of the size specified so that it isn’t accidentally used by over one malloc statement.

  3. The system then places into the pointer variable (p, in this case) the address of the reserved block. *p itself contains an address. The allocated block can hold a value of the type specified, and the pointer points to it.

The program next checks the pointer p to make sure that the allocation request is successful with the line if (p == 0) (which could have also been written as if (p == NULL) or even if (!p). If the allocation fails (if p is zero), the program ends. If the allocation is successful, the program then initializes the block to the value 5, prints out the value, and calls the free function to return the memory to the heap before the program ends.

free() in C is used to dynamically “de-allocate" the memory. The memory allocated using functions malloc() and calloc() is not de-allocated on its own. Hence the free() method is used, whenever the dynamic memory allocation takes place. It helps to reduce wastage of memory by freeing it. The variable to be freed is passed in the parenthesis. See the above code block. malloc() has its twin brother calloc() and twin sister realloc(), let’s see what the siblings have for us

Calloc( )

calloc or “contiguous allocation” method in C is used to dynamically allocate the specified number of blocks of memory of the specified type. It is very much similar to malloc() but has two different points and these are: i) It initializes each block with a default value ‘0’. ii) It has two parameters or arguments as compared to malloc()

/* n = number of elements
 * element_size = the size of each element
 */
ptr = (cast-type*)calloc(n, element_size);

realloc( )

This function attempts to resize the memory block pointed to by a pointer that was previously allocated with a call to malloc or calloc

void *realloc(void *ptr, size_t size)

1. ptr − This is the pointer to a memory block previously allocated with malloc, calloc or realloc to be reallocated. If this is NULL, a new block is allocated and a pointer to it is returned by the function. 2. size − This is the new size for the memory block in bytes. If it is 0 and the pointer points to an existing block of memory, the memory block pointed by pointer is de-allocated and a NULL pointer is returned.

NOTE

It is very important to check that the pointer is zero after each allocation. Since the heap varies in size constantly depending on which programs are running, how much memory they have allocated, etc., there is never any guarantee that a call to malloc will succeed. You should check the pointer after any call to malloc to make sure the pointer is valid.

Delete|Free the malloced block of memory before the program ends. When a program ends, the operating system “cleans up after it,” releasing its executable code space, stack, global memory space, and any heap allocations for recycling. Therefore, there are no long-term consequences to leaving allocations pending at program termination. However, I think it is not the best coding technique, and “memory leaks” during the execution of a program are harmful.