Next:Sample Programs Up:Main Previous:Examples of Arrays and Pointers

Dynamic Memory Allocation

When we declare an array, we need to reserve some memory to store the elements of this array.  This memory allocation and it is static. That is when we declare an array, we specify the number of elements in that array and a fixed  memory is allocated.  Once declared the size of the array cannot be changed.

But there are situations in which  we may  want to declare the variable and not allocate memory for it until we use that variable.  We may also want to free the memory allocated for a variable after its use is over. These are some of the things we will concentrate on in this lecture.

The dynamic allocation of memory during the program execution  is achieved through two built in functions malloc or calloc, realloc and free. There is also sizeof() function used to determine the number of bytes occupied by an entity in memory. Let is look at the use of these functions  through a couple of  examples.

 

Dynamic_memory.c

#include<math.h>

#include<stdio.h>

#define l 4

main()

{

             int *a, i,*p;

            float *b,*c;

            a=(int*) malloc(l);

            for (i=0; i<l; i++)

           {

                        *(a+i)=i;

}

            for (i=0; i<l; i++)

            {

                         printf ("a %d %d %u \n",i,*(a+i),(a+i));

            }

             p=(int*) calloc(l,4);

             for (i=0; i<l; i++)

         {

*(p+i)=i*2;

}

            for (i=0; i<l; i++)

            {

                        printf(" p %d %d %u %d\n",i,*(p+i),(p+i),sizeof(p));

            }       

b=(float*)malloc(l);

 for(i=0;i<l+2;i++)

          {

*(b+i)=i*4.0;

}

            for(i=0;i<l+2;i++)

            {

                          printf(" b %d %f %u %d\n", i,*(b+i),(b+i),sizeof(b));

            }

 c= calloc(l,8);

 for(i=0;i<l;i++)

           {

*(c+i)=i*4.0;

}

 for(i=0;i<l+1;i++)

 {

                         printf(" c %d %f %u %d \n", i,*(c+i),(c+i),sizeof(c));

 }

            for(i=l+1;i<l+2;i++)

 {

             printf(" b %d %f %u \n", i,*(b+i),(b+i));

 }

 }

 

In the program above we create an integer pointer *a and two floating point pointers *b,*c.  Now we want to store a string of numbers using these pointers, instead of using a fixed size array. 

To achieve this we fist have to allocate enough space in the memory to which the pointers point to.  We will allocate just enough memory to store the elements we want to . For example in the above program we want to store 4 integer values.  By using the malloc  function we will allocate memory for an integer array of size 4. Note the usage of malloc () here.  We invoke it by the statement a=(int*) malloc(l);   We could also do the same with p=(int*) malloc(l*sizeof(int));  or p=(int*) malloc(l*size(a));  where l=4. One important thing to note here is that since malloc returns a  void pointer,  we need to type cast it before equating it to the pointer we want. In the case above a  is an integer so we have to use (int *) before malloc. Similarly we see the use of a malloc to allocate memory for a float pointer b . We use the pointer p to demonstrate that we can do exactly the same using the function calloc.  The function is exactly the same , except that we need not type cast it now and also that we have to give a second argument to calloc,which is the number of bytes need to store one variable. In most of the modern compiler this value is ignored by the function.

So far we have seen operations which is pretty much like that in the fixed size arrays. The only difference is that we assign the memory only just before the variable is used. This by itself is not very useful unless we can change this memory allocation , that is increase it if we want more and remove it if we don't want to use the variable again in the program. This is what realloc and free will do for us. Let us look at that with an example.

realloc.c

/*      Dynamic memory allocation realloc and free */

        #include<math.h>

        #include<stdio.h>

        #define l 5

        main()

        {

        float *a;

        int i;

 

        a=(float*) malloc(l);

        for(i=0;i<l;i++)

            { *(a+i)=i*3.0;}

        for(i=0;i<l;i++)

        {

         printf("a %d %f %u \n",

                   i,*(a+i),(a+i));

        }

       a=(float*)realloc(a,sizeof(float)*(l+3));

        for(i=0;i<l+3;i++)

            { *(a+i)=i*3.0;}

        for(i=0;i<l+3;i++)

        {

         printf("a %d %f %u \n",

                   i,*(a+i),(a+i));

        }

 

        free(a);

        }

 

In the above program we have a pointer a which is of type float . We then give it enough momory space to store 5 elements. After filling and printing out the address of those locations and the value stored in them we increase  the"size" of the memory allocated by the statement "a=(float*)realloc(a,sizeof(float)*(l+3));". This statement will increase the memory space to store 3 more floating points. You can see by yourself that if you remove this line and try to store more than5 elements into a you will get segmentation fault. The last statement in the program "free(a);" free all the space allotted to a

Thus by using malloc, realloc and free we can change the memory taken up by the program as required by it. This is particularly useful when we have to use large temporary arrays in a calculation.

Functions  and pointers to functions.

            We saw how to pass array to a function, return array from a function, and declare arrays, pointer arrays, pointers to arrays and also dynamic memory allocation. We will no see a different use of pointers, that is to invoke function calls.  Before we go on to the the description of pointer to a function, lets have quick review of the structure of functions

 

All functions have the following  components.

{ local variable declarations;

       statements;}

 

Two ways of passing variables to a function  are
     (1) call by value

                  Here the function work with a copy of the variable. Changes made in the function will not effect its value in the calling function

     (2)  call by reference

                  Here the address of the variable is passed to the function. Changes made in the function will change its value in the calling function.

 

Let us look at an example to remind ourself about these differences.

#include<math.h>

#include<stdio.h>

#define l 4

main()

{

            float b; int a[2],*c;

            void myfunction();

            c=(int*)malloc(1);

            b=10.0;

            *c=5;

             a[0]=1;a[1]=2;

             printf ("before function  call  %f %d  %d %d\n", b,a[1],a[2],*c);

            myfunction (b,a,c);

            printf ("after  function   call  %f %d %d %d\n", b,a[1],a[2],*c);

 }       

 

void myfunction (x,y,d) 

 float x;

 int y[2],*d;

{

          float z;

          x=2*x;

          y[0]=3*y[0];

          y[1]=3*y[1];

          *d=*d+2;

 }

 

In this example the main function is passing "b,a and c" to "myfunction", which receives it as "x,y and d ".  Since a  is and array and c  is a pointer they are passed by "reference" while b is passed by value.  Inside myfunction all the values are altered. We will printout the values of  "b,a and c" in the main function before and after the call to myfunction. This is what we get,

 

before function  call  10.000000 1 2 5

after    function  call  10.000000 3 6 7

 

 We see that the variable that is passed by value does not get altered while any changes made to quantities, that are passed by reference, in the sub function will alter its value in the main function as well.

 

Pointers to Functions

On may occasions we may want to write a program to which a user defined function can be passed. For example , as you will see later in this course, one can write a general program to solve an ordinary differential equation. We would then like to have it in such a way that a user can write a function, which evaluate all the derivatives of the particular problem, and pass it to the ODE solver. We will now see how this can be achieved by using a pointer.

 

Function name can be used as a pointer  to point to functions like the name of the array is a pointer to its base address. We can also define a pointer, point it to a function and then use that pointer to invoke the function.  The pointer to a function should be of the same "type" as the function.

 If we have to pass a function pointer to another function, we have to define them as "external". That is they are "global" to the program and is defined outside any function boundaries. Lets look at an example for this now.

 

/*Pointer to a function and passing the pointer to a function */

  passing-function.c

/*      Pointer to a function and passing the pointer to a function */

        #include<math.h>

        #include<stdio.h>

        #define l 4

       float cube(float);

        main()

        {

        float b;

        void (*func_ptr)();

        void myfunction();

        b=2.0;

        func_ptr=myfunction;

        (*func_ptr)(b,&cube);

        }

 

 

        void myfunction (x,powerthree)

         float x;

         float (*powerthree)(float);

       {

          float z;

          x=2*x;

          z=(*powerthree)(x);

           printf("%f\n", z);

}

 

  float cube(x)

   float x;

  {

    float z;

    z=x*x*x;

    return z;

  }

 

Here cube is an external function  and is defined outside of the function boundaries. This function calculates the 3rd power of any floating point variable passed to it and returns it as a floating point. Thus this function is of the type "float".

 In the main function we have defined a pointer func_ptr. Since we want to use it to a function which is of the type "void" we need to define this pointer also as "void".

The function myfunction  has a floating point and a function pointer as its arguments. Instead of invoking this function directly, we use func_ptr  for it. Note that we are passing the address of the external function, cube, as an argument here. myfunction receives it as a floating point x and function powerthree.  When powerthree is called inside this function, it is actually invoking the function cube, which is supplied as external.

We thus see the use of function pointer and also the way to pass a function to another function here. We will make use of them later in the coarse while solving ordinary differential equations.

 

 

Next:Sample Programs Up:Main Previous:Examples of Arrays and Pointers