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.
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.
/*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.