DYNAMIC MEMORY ALLOCATION

If a program needs a table of data, but the size of the table is variable, perhaps for a list of all file names in the current directory, it is inefficient to waste memory by declaring a data table of the maximum possible size. Rather it is better to dynamically allocate the table as required.

Turbo C allocates RAM as being available for dynamic allocation into an area called the "heap". The size of the heap varies with memory model. The tiny memory model defaults to occupy 64K of RAM. The small memory model allocates upto 64K for the program/code and heap with a far heap being available within the remainder of conventional memory. The other memory models make all conventional memory available to the heap. This is significant when programming in the tiny memory model when you want to reduce the memory overhead of your program to a minimum. The way to do this is to reduce the heap to a minimum size. The smallest is 1 byte.

C provides a function malloc () which allocates a block of free memory of a specified size and returns a pointer to the start of the block; it also provides free() which deallocates a block of memory previously allocated by malloc(). Notice, however, that the IBM PC doesnot properly free blocks of memory, and contiuous use of malloc() and free() will fragmentise memory, eventually causing no memory to be available untill the program terminates.

This program searches a specified file for a specified string (with case sensitivity). It uses malloc () to allocate just enough memory for the file to be read into memory.

#include <stdio.h>

#include <stdlib.h>

char *buffer;

void main(int argc, char *argv[])

{

FILE *fp;

long flen;

/* Check number of parameters */

if (argc != 3)

{

fputs("Usage is sgrep <text> <file spec>",stderr );

exit(0);

}

/* Open stream fp to file */

fp = fopen(argv[2],"r");

if (!fp)

{

perror("Unable to open source file");

exit(0);

}

/* Locate file end */

if(fseek(fp,0L,SEEK_END))

{

fputs("Unable to determine file length",stderr );

fclose (fp);

exit(0);

}

/* Determine file length */

flen = ftell(fp);

/* Check for error */

if (flen == -1L)

{

fputs("Unable to determine file length",stderr );

fclose (fp);

exit(0);

}

/* Set file pointer to start of file */

rewind(fp);

/* Allocate memory buffer */

buffer = malloc (flen);

if (!buffer)

{

fputs("Unable to allocate memory",stderr );

fclose (fp);

exit(0);

}

/* Read file into buffer */

fread(buffer,flen,1,fp);

/* Check for read error */

if(ferror(fp))

{

fputs("Unable to read file",stderr );

/* Deallocate memory block */

free(buffer);

fclose (fp);

exit(0);

}

printf ("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was found" : "was not found",argv[2]);

/* Deallocate memory block before exiting */

free(buffer);

fclose (fp);

}

VARIABLE ARGUMENT LISTS

Some functions, such as printf (), accept a variable number and type of arguments. C provides a mechanism to write your own functions which can accept a variable argument list. This mechanism is the va_ family defined in the header file 'stdarg.h'.

There are three macros which allow implementation of variable argument lists; va_start(), va_arg() and va_end() and a variable type va_list which defines an array which holds the information required by the macros.

va_start() takes two parameters, the first is the va_list variable and the second is the last fixed parameter sent to the function. va_start() must be called before attempting to access the variable argument list as it sets up pointers required by the other macros.

va_arg() returns the next variable from the argument list. It is called with two parameters, the first is the va_list variable and the second is the type of the argument to be extracted. So, if the next variable in the argument list is an integer, it can be extracted with;

<int> = va_arg(<va_list>,int);

va_end() is called after extracting all required variables from the argument list, and simply tidies up the internal stack if appropriate. va_end() accepts a single parameter, the va_list variable.

The following simple example program illustrates the basis for a printf () type implementation where the types of the arguments is not known, but can be determined from the fixed parameter. This example only caters for integer, string and character types, but could easily by extended to cater for other variable types as well by following the method illustrated;

#include <stdarg.h>

char *ITOS(long x, char *ptr)

{

/* Convert a signed decimal integer to a string */

long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };

int n;

/* Check sign */

if (x < 0)

{

*ptr++ = '-';

/* Convert x to absolute */

x = 0 - x;

}

for(n = 0; n < 9; n++)

{

if (x > pt[n])

{

*ptr++ = 48 + x / pt[n];

x %= pt[n];

}

}

return(ptr);

}

void varfunc(char *format, ...)

{

va_list arg_ptr;

char output[1000];

char *ptr;

int bytes;

int x;

char *y;

char z;

/* Initialise pointer to argument list */

va_start(arg_ptr, format);

/* loop format string */

ptr = output;

bytes = 0;

while(*format)

{

/* locate next argument */

while(*format != '%')

{

*ptr++ = *format++;

bytes++;

}

/* A % has been located */

format++;

switch (*format)

{

case '%' : *ptr++ = '%';

break ;

case 'd' : /* integer expression follows */

x = va_arg(arg_ptr,int);

ptr = ITOS(x,ptr);

*ptr = 0;

format++;

bytes += strlen(output) - bytes;

break ;

case 's' : /* String expression follows */

y = va_arg(arg_ptr,char *);

strcat(output,y);

x = strlen(y);

format++;

ptr += x;

bytes += x;

break ;

case 'c' : /* Char expression follows */

z = va_arg(arg_ptr,char);

*ptr++ = z;

format++;

bytes++;

break ;

}

}

/* Clean stack just in case! */

va_end(arg_ptr);

/* Null terminate output string */

*ptr = 0;

/* Display what we got */

printf ("\nOUTPUT==%s",output);

}

void main()

{

varfunc("%d %s %c",5,"hello world",49);

}

A simpler variation is to use vsprintf() rather than implementing our own variable argument list access. However, it is beneficial to understand how variable argument lists behave. The following is a simplification of the same program, but leaving the dirty work to the compiler;

#include <stdio.h>

#include <stdarg.h>

void varfunc(char *format, ...)

{

va_list arg_ptr;

char output[1000];

va_start(arg_ptr, format);

vsprintf(output,format,arg_ptr);

va_end(arg_ptr);

/* Display what we got */

printf ("\nOUTPUT==%s",output);

}

void main()

{

varfunc("%d %s %c",5,"hello world",49);

}

 

 

 

Links:  Home C Programming Guide C++ programming Guide

Contact Me