Dynamic Allocation of Memory and malloc()


Conventional array and other data declarations require that the compiler knows how much memory to allocate. This means that arrays declared using approaches such as:

float fa[N];

require that N is a constant known at compile time. The following approach doesn't work:

int size;
float *fp;
printf("enter number of elements\n");
scanf("%d",&size);
fp=makearray(size);
...
float *makearray(int size){
   static float fa[size]; /* wrong !! */
   return fa;
}
This restriction prevents this method of data declaration being used for variable sized data structures. However there exist situations where a program is required to optimise use of memory for an application. Flexibility requires that the source code should not need to be changed to match the amount of data to the memory available and recompilation with different sizes for data structures might not be practical. For example, you might wish to use the same database program on a mainframe capable of handling millions of records or on an embedded system where memory only exists for a few thousand. A more flexible software design approach requires dynamic memory allocation.

Other programming languages with object oriented features typically use the new keyword to allocate space for, and return references to non-trivial newly created objects. The 'C' programming language uses the malloc() function for this purpose.
 

JAVA example:

BufferedReader in=Text.open(System.in); // allocate input stream, requires Text class
Text.prompt("how many frequencies ?"); // prompt user
int numf=Text.readInt(in); // read number of frequencies required
int frequencies[] = new int[numf]; // frequencies is now an array of integers

'C' example 1:

int *frequencies, numf; /* pointer to start of and size of array */
printf("how many frequencies ?\n");   /* prompt user */
scanf("%d",&numf); /* read number of frequencies required */
frequencies = (int*) malloc(sizeof(int)*numf); 
  /* frequencies now points to the start of an array of integers */
Let's examine the prototype for the function malloc() :

void *malloc(size_t);

malloc() requires one parameter of type size_t, this is an integral type such as might be returned by sizeof(). With Borland 'C' this parameter is the amount of memory which malloc() is required to allocate in bytes. malloc() returns a void pointer, which is a valid address to the contiguous block of memory allocated, but of no specific type, because malloc() doesn't know what data type the memory allocated will be used for.

In the above example 1:

frequencies = (int*) malloc(sizeof(int)*numf);

 The void pointer returned is cast to become a pointer to int using the (int*) cast operator, so that the memory allocated will be used to store integers. The size of an integer is returned by sizeof(int), which is 2 bytes in Borland 'C'. This is multiplied by numf, the number of integers required, so 2 (numf) bytes are allocated. The pointer frequencies may now be used to access an array of numf integers. The first integer is addressed as frequencies and accessed as frequencies[0]  and the last integer in the array may be accessed as frequencies[numf-1]
 

Example 2:

/* mallstr.c:
 Richard Kay, last changed 13 Dec 1999
*/
#include <stdio.h>
#include <conio.h>
#include <stdlib.h> /* need to include stdlib for malloc()*/
int main(void){
  char *sp; /* space declared for string address, none allocated to string*/
  int i,size; /* size of memory for string */
  printf("enter size of string\n");
  scanf("%d",&size);
  fflush(stdin);
  
  /* allocate memory for string and assign start address to sp*/
  
  sp=(char*)malloc((size_t)size*sizeof(char)); /* buffer of size bytes starting address assigned to sp */
  printf("enter string\n"); /* get string and input it to buffer allocated
  fgets(sp,size,stdin);  /* safer than scanf("%s",sp) as buffer can't overflow */
  /* output string one char at a time */
  printf("pos \tchar \tascii\n");  /* column headings */
  for(i=0;i<size;i++)    /* output position,char and decimal ascii values */
     printf("%d\t%c\t%d\n",i,sp[i],sp[i]);  /* note use of array notation    */
                                            /* sp[i] is the same as *(sp+i)   */
  getch();
  return 0;
}
Memory allocated using malloc() may be freed using free(), after which the pointer variable and the allocated block can both be reused for another requirement. Reuse of the pointer prior to freeing the allocated block would result in that block of memory being unavailable however. Sometimes the term "memory leak" is used to describe this situation, a kind of bug which is difficult to find which can cause a program to run out of memory and crash. malloc() returns a NULL pointer either if 0 bytes of storage were requested or if there is insufficient memory available to meet the request. Programs can test for this and take special action if needed.
 

Example 3:


/* mallocex.c:
    stores height chart using dynamic memory allocation and displays it
    Richard Kay     Last Changed 10 Dec 99 */

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>  /* includes prototype for malloc() */

#define HDATA "heights.txt"

typedef struct {
   char name[30];  /* persons name */
   float height;       /* persons height */
} HRECORD;  /* height record data type */

int countrecs(FILE*);     /* counts records in file */
void storedata(FILE *fp,HRECORD harray[],int rows);  /* stores in dynamic array */
void displaydata(HRECORD harray[],int rows);  /* displays data in array */

int main(void){
   int numrecs;
   char nam[30],buff[BUFSIZ];
   float high;
   FILE *in;
   HRECORD *hrecsp; /* will point to start of dynamic anonymous array */
   numrecs=countrecs(in); /* count number of records in input file */
   printf("there are %d records in the %s file\n",numrecs,HDATA);

  /* Assign a pointer hrecsp to the start of a block of memory taken from the memory heap.
     The pointer is to be used to access  a variable size height records array. malloc() takes a
     parameter for the size of a block of memory to be allocated and returns a void pointer to
     the start of this block. This void pointer is then cast to a pointer of the appropriate type
     for storing the data.  malloc() returns NULL if 0 bytes of memory are requested or
     sufficient memory to meet the request is unavailable */

   if((hrecsp=(HRECORD*)malloc(sizeof(HRECORD)*numrecs)) == NULL){
     fprintf(stderr,"insufficient memory available or no height data\n");
     return 1; /* exit program now */
   }
   storedata(in,hrecsp,numrecs); /* store data in dynamic array */
   displaydata(hrecsp,numrecs);  /* display data in dynamic array */

   free(hrecsp); /* free the memory allocated */
   getch();
   return 0;
} /* end of main() */

int countrecs(FILE *fp){   /* counts number of records in file but doesn't store them */
   int rows=0;
   char buff[BUFSIZ];  /* standard buffer size is defined in stdio.h */
   fp=fopen(HDATA,"rt");
   while(fgets(buff,BUFSIZ,fp) != NULL)  /* what if record longer than buff ? */
   rows++;     /* incremented for each row in file */
   fclose(fp);              /* close it so when we read data we start from beginning again */
   return rows;
}

void storedata(FILE *fp,HRECORD harray[],int rows){
   /* stores data in dynamically allocated array */
   int i;
   char buff[BUFSIZ],nam[30];
   float high;
   fp=fopen(HDATA,"rt"); /* open heights data file again in read mode */
   /* read data into harray  */
   for(i=0;i<rows;i++){
      fgets(buff,BUFSIZ,fp);  /* read next record from file */
      sscanf(buff,"%s%f",nam,&high); /* read name and height from buffer */
      strcpy((harray+i)->name,nam); /* assign element i of array member name */
      (harray+i)->height=high; /* assign element i of array member height */
   }
   fclose(fp);
} /* end of storedata()

void displaydata(HRECORD harray[],int rows){
   /* displays data in dynamically allocated array */
   int i;
   for(i=0;i<rows;i++){
      if(i%20==0){  /* throw a set of headings if needed */
         if(i>0){  /* pause and clear screen unless first page */
    printf("press a key to continue\n");
    getch();
    clrscr();
         }
         printf("name\t\theight\n");     /* Print headings. Note us of \t tabs to*/
         printf("====\t\t======\n"); /* line up columns */
     }
      /* display data for height record         */
      printf("%s\t\t%f\n",(harray+i)->name,(harray+i)->height);
   } /* end for */
} /* end of displaydata() */