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.
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
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]
/* 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.
/* 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() */