14. Remember you're a.......

Welcome to the penultimate non-WIMP installment of the C from the Top series - this time, we'll look at memory.

There are 4 functions in the <stdlib.h> header file do deal with dynamic memory allocation (for those using pre-RISC OS 3.5 machines, don't worry - this is not the same as the dynamic memory areas on OS 3.5 and above!). These functions are (including the prototypes)

  • void *calloc(size_t num, size_t size);
  • void free(void *pointer);
  • void *malloc(size_t size);
  • void *realloc(void *pointer, size_t size);
The important aspect to remember is that all (with the exception of free) are pointers - thinking about it, you would expect it as to use one of these, you use a pointer variable, which as you know, points to a block of memory!

Another good point about the three alloc functions is that they return the first byte of the allocated area. If this returns NULL then we know the machine doesn't have enough memory or for some other reason, the allocation has failed.

In code, this requires 4 lines of code to check if NULL has been returned

if (!pointer) 
{
 printf("Allocate failed\n");
 exit(1);
}

These 4 lines can help avoid the dreaded fatal error of not enough memory on the stack.

The heap?

The heap can be thought of a large pool of water which the program "drinks" from. When the pool is empty, the program can drink no more and goes thirsty. The thirsty program then dies or tries to grab memory from elsewhere (which is what happens in Windows giving you the either the blue screen of death or General Protection Fault).

Due to hardware restrictions, on all current RISC OS machines (e.g. Acorn and RiscStation) we are limited to 28Mb - hopefully RISCOS Ltd will have a hardware independent version for when this hits the stands, until then, we have to make do with 28Mb.

What the functions do

  • calloc allocates a block of memory equal to num. This allocation is sufficient for an array of num objects, size long, where size is the sizeof(var_type).
  • free frees up the stack which pointer has had allocated to it. It is vitally important that after a block of memory has been finished with, that the free function is called. This is one of the most common methods of memory leak encountered. Gone unchecked, this can lead to the program ultimately failing.
  • malloc allocates a block of memory of size size. malloc is very useful in the WIMP system - for example, below would be how to use one of the swi commands as a struct - it is then allocated to a block of memory within the main part of the program. This is actually a very good example on how to use and create your own header files.
struct {
        char log2sectorsize;
        unsigned char sectors_per_track;

/* I've snipped a large chunk out here as it's not really needed 
   to demonstrate the point!*/

       }  disc_record,*block;

then in the main program

block=malloc(sizeof(disc_record));

That's nice and simple, but how on earth do you either read or write to the block of memory used?

You would do it in exactly the same way as you would with any normal structure, by using

struct_block.var_name =

to allocate the value or

struct_block->var_name

to read the variable name - for instance.....

printf("log2sectorsize : %d\n",block->log2sectorsize);
printf("sectors per track : %d\n",block->sectors_per_track);

and

regs.r[4]=block->disc_size;

realloc changes the size of the allocated memory (from pointer) to the new size, size. A pointer to the memory block is returned as it is possible that realloc to move the block for the size increase to occur. If there is enough memory, everything is transferred otherwise, a NULL is returned. THIS FUNCTION MUST BE CHECKED FOR THE RETURN VALUE.

Strictly speaking, whenever you create a string, you should dynamically allocate the space to the array rather than have the computer do it for you.

This would be carried out thus

char *name1, *name2;
name1=(char*) malloc(9);
name2=(char*) malloc(9);

name1 and name2 are then treated as any other string variable, only 10 bytes have been assigned (0 to 9 inclusive) instead of assigning a large array to cope with the input.

From what I can see, there is only one advantage over using a system such as this - you are assured of allocating the memory to the variable (of course, this supposes the memory is there in the first place!)

Command me......

So far, when we have created a program, we've not had any arguments to pass to the main() function. Sometimes though, command line programs can come in very useful.

While one may argue that in the days of the WIMP, command line programs should not need to be used, it can also be argued that for personal use, or when the program is not that complex, a command line program has a larger number of attractors than detractors (such as speed of writing and speed of both execution and debugging).

How to do it.

The normal prototype for main() is

int main(void)

however, for a command line program, this has to be changed to

int main(int argc, char *argv[]}

When this is called, argc returns the number of arguments given. In a non command line program this is 1 (the command itself), however in a command line program, argc still reports the number of arguments given, but remember that argv is a pointer to an array, therefore the first argument (the command) is argv[0] with the final one being argv[n].

The program below demonstrates this

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
 int loop;
 if (argc==1)
 {
  printf("You have given me %d argument\n",argc);
  printf("That is %s",argv[argc-1]);
  exit(1);
 }
 else
 {
  printf("You have given me %d arguments\n",argc);
  printf("They are : \n");
  for (loop=0;loop<argc;loop++)
  printf("%s\n",argv[loop]);
 }
 return 0;
}

this can get a tad confusing - just remember that if you have a program needing 3 arguments, the last argument (argc==3) will be the last argument - 1 in argv (argv[2]).

You should also check within the program to ensure that the correct number of arguments have been passed over to in the command line. It would be of no use to run a jpeg viewer without telling the program which jpeg to view, or a copier with not telling the copier what to copy.

Say you had a program which required the following arguments

file_copy <source> <dest.>

you need to check firstly that the correct number of arguments have been passed before you check if <source> and <dest.> actually exit.

As you know, argc holds the number of arguments passed in the command line, therefore code such as

if (argc!=3)
{
 printf("Usage\n");
 printf("file_copy <source> <destination>\n");
 exit (1);
}

should be used.

The final tutorial before hitting the WIMP will be on some bits and pieces which I have left out until now for a good reason - they didn't quite fit in anywhere else!

Until then, keep on writing your C programs. Remember, they may be all CLI material, but the more you write, the better you will become.