Dynamic Memory Allocation in C

Dynamic Memory Allocation in C

Dynamic memory allocation is allocation of memory only when it is needed, that is, at run time (when the program is running). It enables us to create data types and structures of any size and length to suit our program’s need within the program.

Using static memory allocation in C, as in arrays, we had to tell in advance how much space we want. But it was disadvantageous in many ways :

  1. later on we come to know that the memory we demanded is less than that is needed by the ways.
  2. later on we come to know that the memory we demanded is much more than that is needed by the program.
  3. we cannot give the dimensions of the array at runtime, which most of the mathematical and scientific applications demand.
  4. we cannot take a temporary array inside a function, whose size matches the size of the array passed to the function.

in the above situations, we are helpless to either supply more memory, or to cut the extra memory, or to give array dimensions at run time, or to declare a temporary array with size equal to the array to a function. Therefore, to keep  the balancing of the memory (so that the memory needed is neither less nor extra  than is needed), or to make provisions for allocating memory to the array at runtime, we make request for the memory dynamically (i.e. on the fly) using dynamic memory allocation.

That is, when it is needed. Since this memory is allocated from a memory area called heap or free store, it must be released after our work is done so that when any other program also wants some chunk of memory, it is freely given to it. Two common applications of dynamic memory allocation are :

  • Dynamic arrays
  • Dynamic data structure (such as linked lists, binary trees, etc.)

Dynamic Memory Allocation uses mainly three standard library functions declared in the header file alloc.h which  are-

  • malloc()
  • calloc()
  • realloc()

To de-allocate (or release) the dynamically allocated memory in dynamic memory allocation, there is one standard library function declared in the header file alloc.h , which is

  • free()

Dynamic memory allocation in c Language just uses these four functions to implement it effectively.

Dynamic Memory Allocation using malloc() and sizeof()

Malloc function is most commonly used to try to grab a continuous portion of  memory. Its prototype is :

        void *malloc (size_t number_of _bytes) ;

It means that malloc returns a pointer of type void *. This pointer is the starting address of the memory of the reserved portion of size number_of_bytes. If memory cannot be allocated, a NULL pointer is returned. Since a void * is returned, it means that this pointer can be converted to any type (even user-defined type). The size_t argument type is defined in stdlib.h and is an unsigned type.

So,

     char *p;

     p = malloc(50) ;

tries to get 50 bytes and assigns the starting address of the reserved portion to p.

Also it is usual to use the sizeof() function to specify the number of bytes :

 int *p ;

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

Some C  compilers may require casting the type of conversion. The (int*) means coercion (or, typecasting) to an integer pointer. Coercion to the correct pointer type is very important to ensure pointer arithmetic is performed correctly. It is good practice to use sizeof( ) even if we know the actual size of the type we want. It makes for device independent (portable) code because the size of the types may differ from machine to machine.

sizeof() can be used to find the size of any data type, variable or structure.

So, with following declarations

int i;

struct Point{int x,y};

typedef struct Point P ;

struct Person{char name[25]; float age, income;};

typedef struct Person employee;

all the following expressions are valid :

        sizeof(int),

        sizeof(i),

        sizeof(struct Point)

        sizeof(P)

        sizeof(struct Person)

        sizeof(employee)

In the above we can use the link between pointers and arrays to treat the reserved memory like an array. i.e. we can do things like :

        p [0] = 10;

            or

        for (i=0; i<10;++i) scanf (“%d”,p++);

 

free()

 When we have finished using the portion fo memory we requested (by using malloc(), we must always free() it. This allows the memory freed to be available again, possibly for other programs to make further malloc() calls. The function free() takes a pointer as an argument and frees the memory to which the pointer refers.

That is, if  we requested a chunk (or, portion) of memory using malloc() as,

        int *ptr;

        ptr=(int*) malloc(sizeof(50*int));

then we must free the allocated area by using the call

        free(ptr) ;

 

calloc and realloc in Dynamic memory allocation

There are two more memory allocation functions, calloc() and realloc() . Their prototypes are given below :

        void *calloc(size_t num_elements, size_t element_size);

        void *realloc(void *ptr, size_t new_size);

  • malloc does not initialize memory (to zero) in any way. If you wish To initialize memory then use calloc. calloc is slightly more computationally expensive but, occasionally, more convenient than malloc. Also the syntax between calloc and malloc is different in that calloc takes the number of desired elements, num_elements, and element_size, as two individual arguments.

 

Thus to assign 100 integer elements that are all initially zero you would do :

int *p;

p=(int*) calloc(100, sizeof(int) ) ;

 

try the following program to check it out:

#include<stdio.h>
#include<alloc.h>
main( )
{
     int *p, i;
     p= (int *) calloc(10, sizeof(int));
     for (i=0;i<10;i++) printf(“%d “,*(p+i));
}

OUTPUT :

0 0 0 0 0 0 0 0 0 0

  • realloc is a function which tries to change the size of a previous allocated block of memory. The new size can be larger or smaller. If the block is made larger, then the old contents remain unchanged.

If the original block size cannot be resized, then realloc will attempt to assign a new block of memory and will copy the old block contents [and so, a new pointer (of different value) will be returned]. We must use this new value. If new memory cannot be reallocated, then realloc returns NULL.

For example, to change the size of memory allocated to the *p pointer above to an array block of 50 integers instead of 10, simply do :

ip = (int *) realloc(ip,50);

 

Limitations of  arrays in C

There are some aspects where C can’t boast of that it is a strong language. The two weak points of C language are:

  • Floating-point numbers cannot be represented accurately and precisely, and there may be round-off and other errors whenever we deal with floating-point numbers in C language. The following programs tell the true story :
#include<stdio.h>
#include<alloc.h>
main( )
{
float  x=1.1;
if(x==1.1)
          printf(“C is good in representing floating-point numbers…”);
else          
          printf(“C is poor in representing floating-point number…”);
}

Output: C is poor in representing floating-point numbers…

 

#include<stdio.h>
#include<alloc.h>
main()
{
float  f1 = 1000000456,   f2= 1000000123,  f3;
f3   =   f1  -  f2;
printf(“%f\n”,f3);
}

Output : 320

Both of the above programs give unexpected results when executed. This is the weakness of C’s representation of floating-point numbers.

  • The memory to the arrays built in C is not allocated dynamically (i.e. at run time). That’s

why we have to give  the dimensions  of C arrays at the time of coding, and not when the program runs. Further, there is no bound-checking on array, and to add more, we have to  start array subscript from 0 whereas most of the mathematical and scientific applications start the subscript from 1.

That’s why C’s support for scientific programming is not that strong. We will discuss here how memory can be allocated dynamically to the arrays in C language, and how array subscription can be changed to 1 instead of 0. This way, we can give the dimensions of the array at run time, and can handle mathematical and scientific calculations with more convenience.

Problem in passing multi dimensional arrays to a function

When we deal with 1-D arrays in C, it is clear that when a function receives a 1-D array as a parameter, as in

func(int a[])                                /*called function*/

{

….

}

main()                               /*caller function*/

{

int a[] = (1,2,3,4,5);

f(a);          /* a is a pointer to the beginning of the array*/

}

then there is no need to specify the size of the array. This is so because the function func isn’t  allocating the array and does not need  to know how big it is . The array is actually allocated in the caller function. The caller function sends only a pointer to the beginning

of the array is passed to the called function like func. If in the function func, we need to know the number of elements in the 1-D array, we have to pass a second parameter, as show below :

func(int a[], int n)

{

}

We can also write the function func as, 

func(int *a, int n)

{

}

to show that what we are passing in really a pointer.

Thus, in the case of a 1-D array, it’s plus point of C that we do not need to specify the size of the 1-D array. Therefore, if the function func is used to do something generic like “find the largest element in the array,” then we can use the function func to call it with arrays of any size. Following example proves this point :

#include<stdio.h>
#include<alloc.h>
func(int *a, int n)
{
int I, big;
for(i =o; I <n; i++)
{
if(i ==1) big = a[i];
if(a[i]>big) big =a[i];
}
return big;
}
main()
{
    int a[] = (1,2,3,4,5,6);
    int b[] = (7,8,9);
    int c[] = {12,34,32,21};
    printf(“Largest element of array a=%d”, func(a, 6));
                                                   /* func () called with size 6 */
    printf(“ Largest element of array b=%d”, func(b,3 ));
                                                  /*func() called with size 3 * /
    printf(“ Largest element of array c=%d”,func(c,4));
                                                 / * func () called with size 4 */
}

If instead of the above function, we had to write another like

func1(int a[6])                        /* 1-D array with dimension specified */

{

 …..

}

then, it would be a ridicule because func1() could only operate on arrays of size 6 (and not of size 10, or any other size!); In this case , we’d have to write several different versions of func1(), one size for each size.

But, with 2-D arrays and multi-dimensional arrays, the situation is not that straightforward. For example, suppose we want an array

int arr[5][10];

to be passed to a function max(), then how will max() receive this array ?One way is to declare max() as

max(int arr[5][10])

{

…..

}

but that is not a good solution, as dimensions of the array are fixed at compile time and the above function will work only for 2-D arrays of 5 rows and 10 columns. We can argue that if a 1-D array can be passed to a function by declaring it like

f(int *arr)

{

….

}

but this is incorrect.

The reason is that the array arr that we’re trying to pass to max is not really a multi dimensional array. Rather, it is an “array of arrays”, or a 1-D array of 1-D arrays). Therefore, what max() receives is a pointer to the first element of the array, but the first element of the array it itself an array! Therefore, max() receives a pointer to an array, So, its actual declaration should look like this:

max(int (*arr)[10])

{

}

The compiler will take is as if we had written arr[5][10] or arr[][10] . int (*arr)[10] says that the parameter arr is a pointer to an array of 10 ints. Still now, we can use normal array subscripts. For example, still now arr[i][j] is the jth element in the ith array pointed to by arr. The problem here is that we still have to specify the second dimension. That is, we cannot write like

max(int arr[][] )  {—}

or,

max(int(*arr)[])   {—}

 

Dynamic Arrays in C

Dynamic arrays are not actually arrays in the true sense because of the way pointers and arrays work in C. Rather they are pointers to pointers, which are being treated  as arrays by using the subscript notation[].

Example:

#include<stdio.h>
#include<alloc.h>
typedef double ** matrix;
typedef double *vector;
typedef double elem;
vector alloc_1d(int n)
{
vector vec=malloc(n *sizeof(elem));
return vec;
}
matrix alloc_2d(int m, int n)
{
int i;
matrix mat;
mat=malloc(m*sizeof(vector));
for(i=0;i<m;i++)
  mat[i]=malloc(n*sizeof(elem));
return mat;
}
/*To free 1_d array */
void free_1d(vector vec)
{
free(vec)
}
/* To free 2-d array */
void free_2d(matrix mat, int m)
{
int i;
for(i=0;i<m;i++)
{
        free(mat[i]);
free(mat);
}

 

Dynamic Data Structures in C

Dynamic data structures are the structures that allow insertion, deletion, modification etc. of data dynamically i.e. at run time. Dynamic data structures can be linked lists, linked stacks and queues, binary tree etc.

Linked lists (Click here to read more details about Linked List)

Define a node as

typedef struct node

{

 type info;

struct node *node;

}Node;

where type is any valid data type and the list would be grown dynamically.

newNode=(Node *) malloc(sizeof(Node));

This will allocate memory for a new node.

To deallocate free() function is used.

free(newNode);

 

Linked Stack

Stack is an Abstract data type which uses the dynamic memory allocation feature of C Language. An ADT is a data structure together with operations performed on it. Stack is a data structure on which data can be inserted and deleted from the same end. That end is called top of the stack. The operation of inserting elements to the stack is called push and deleting is called pop. The data element pushed last in popped first. This stacks works on Last in First Out (LIFO) structure.

Typical operations performed on stack are-

Push – To insert items on the stack

Pop – The delete an item from the stack

Top – To get the item which is at the top of the stack

empty – To test whether stack is empty

full – To test whether the stack is full 

Linked Queue

A queue is a special case of linked list where one data element joins the list at the let end and leaves in an ordered fashion at the other end. Items can be inserted from the one end called rear and the queue and deleted from the other end called front of the queue. The basic operations on the ADT queue are :

insert(Queue, item) : to insert an item from the rear and end of the queue

delete(Queue) : to delete an item from the front end of he queue; returns the deleted items.

front(Queue) : to get the item which is at the front of the queue, returns the item at front of the queue.

empty(Queue) : to test whether the queue is empty; returns a Boolean value

full(Queue) : to test whether the Queue is full; returns a boolean value.

I have covered Dynamic Memory Allocation in  C Language  in details with a touch to Data Structures. It will definitely update your C Programming Knowledge. Pl do comment and share the post, if you like it.

Similar Posts:

You may also like :

GST Invoice Format in Tally.ERP9              Order Processing in Tally.ERP 9                         Reverse Charge Mechanism in Tally.ERP9

Data Types in JavaScript             JavaScript Control Statements and Operators                        JavaScript Functions and Loops

Download Official TurboC  IDE cum Compiler from here

 

0 0 votes
Article Rating
Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback
3 years ago

[…] Dynamic Memory Allocation in C […]

trackback
3 years ago

[…] Dynamic Memory Allocation in C […]

Please Subscribe to Stay Connected

You have successfully subscribed to the newsletter

There was an error while trying to send your request. Please try again.

DigitalSanjiv will use the information you provide on this form to be in touch with you and to provide updates and marketing.
Share via
Copy link
Powered by Social Snap