Archive-name: c-notes/Lesson03.txt

                                    Lesson 3

                               Arrays and Pointers.

   You can allocate space for an array of elements at compile time with fixed
   dimension sizes of any data type, even functions and structs.
   So these are legal array definitions:

  char name[30];                   /* An array of 30 signed characters. */
  char *strings[50];               /* 50 pointers to strings. */
  unsigned long int *(*func)()[20];/* An array of pointers to functions which */
                                   /* return pointers to unsigned long ints. */
   
   You can declare a pointer to point at any type of data element, and as in
   the array situation above functions and structs are included.

struct ship
{
  char name[30];
  double displacement;                           /* in grammes */
  float length_of_water_line;                    /* in meters */
  unsigned short int number_of_passengers;
  unsigned short int number_of_crew;
  };

   So using the ship concept from Lesson 2 you can declare a pointer to point
   at one of the ship structs in an array.

struct ship *vessel_p;

   Note the use of the suffix "_p".
   This is my way of reminding myself that the variable is a pointer.

struct ship fleet[5];     /* This allocates enough storage for 5 ships' info. */

   Now lets set the pointer to point at the first vessel in the fleet.

  vessel_p = fleet;

   This pointer can be made to point at other ships in the fleet by
   incrementing it or doing additive arithmetic on it:

  vessel_p++;             /* point a the next ship in the fleet array. */
  vessel_p = fleet + 3;

   Also we can find out the index of the ship in the fleet at which we are
   pointing:

  i = vessel_p - fleet;
   
   It is also legal to find out the separation of two pointers pointing at
   elements in an array:

  d = vessel_p - another_vessel_p; /* This gives the separation in elements. */

   So summarising, pointers may be incremented, decremented, and subtracted
   one from another or have a constant added or subtracted from them.
	 Any other mathematical operation is meaningless and not allowed.

   Assembler programmers should note that while the pointer variables contain a
   byte machine address, when the arithmetic is done using pointers the compiler
   also issues either a multiply or a divide as well as the add or subtract
   instruction so that the result is ALWAYS expressed in elements rather than
   bytes. Have a go and write yourself a trivial little program, and have a
   look at the compiler output code. Lesson 1 told you how!

   When using a pointer to reference a structure we have to use a "pointer
   offset" operator in order to access the member of the struct we require:

  vessel_p = fleet;

  vessel_p->name = "Queen Mary";
  vessel_p->displacement = 97500000000.0;
  vessel_p->length_of_water_line = 750.0
  vessel_p->number_of_passengers = 3575;
  vessel_p->number_of_crew = 4592;

   Remember:
       
       It's a "." when accessing a struct which is in storage declared in
       the program.

       It's a "->" when accessing a struct at which a pointer is pointing.

  Initialisation of arrays.

   'C' has the facility to initialise variables in a program script.

   Some examples:

  char *qbf = "The quick brown fox jumped over the lazy dogs back";

  int tic_tac_toe[3][3] =
  {
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 }
    };

  struct ship fleet[2] =
  {
    { "Queen Elizabeth",  97500000000.0, 750.0, 3575, 4592 },
    {      "Queen Mary", 115000000000.0, 875.0, 4500, 5500 }
    };

   Take a careful note of where the commas and semi-colons go ( and don't go )!

   Initialised Tables of Indeterminate Length.

   One nice feature 'C' offers is that it is able to calculate
   the amount of storage required for a table by 'looking' at the number
   of initialisers.

char *verse[] =
{
  "On top of the Crumpetty Tree",
  "The Quangle Wangle sat,",
  "But his face you could not see,",
  "On account of his Beaver Hat.",
  "For his Hat was a hundred and two feet wide.",
  "With ribbons and bibbons on every side,",
  "And bells, and buttons, and loops, and lace,",
  "So that nobody ever could see the face",
  "Of the Quangle Wangle Quee."
  NULL
  };

   Note the * character in the definition line. This means that we are going
   to make an array of pointers to variables of type char. As there is no
   number between the [ ] characters the compiler calculates it for us.
   With this kind of set-up it is nice and easy to add extra information
   to the table as program development proceeds. The compiler will calculate
   the new dimension for you. The point to remember is that the program has to
   know - from the contents of the table - that it has come to the end of the
   table! So you have to make a special entry which CANNOT under any
   circumstances be a real data element. We usually use NULL for this.
   The other way is to calculate the size of the table by using the sizeof
   operator - Note that although use of sizeof looks like a function call
   it is in fact an intrinsic operator of the language. The result is
   available at compile time. So to obtain the size of the array of pointers
	 in bytes one can say:-

#define SIZE_OF_VERSE ( sizeof ( verse ))

   Now that on its own is not very useful, we really want the size in
	 elements - for use in "for" loops etc. - so we can say:-

#define VERSE_NO_ELEMENTS sizeof (( verse ) / sizeof ( verse[0] ))

   There is one final initialised data type, the enum. It is a fairly recent
   addition to the language.

  enum spectrum { red, orange, yellow, green, blue, indigo, violet } colour;

   In this construct the first symbol is given the value of 0 and for each
   following symbol the value is incremented. It is however possible to assign
   specific values to the symbols like this:

  enum tub
  { anorexic = 65,
    slim = 70,
    normal = 80,
    fat = 95,
    obese = 135
    };

   Some compilers are bright enough to detect that it is an error if an
   attempt is made to assign a value to an enum variable which is not in
   the list of symbols, on the other hand many are not. Take care! In
   practice there is little difference between the enum language construct
   and a number of define statements except perhaps aesthetics. Here is
   another trivial program which demonstrates the use of enum and a
   pre-initialised array.

 /* -------------------------------cut here--------------------------------- */

#include <stdio.h>

enum spectrum { red, orange, yellow, green, blue, indigo, violet } colour;

char *rainbow[] = { "red", "orange", "yellow", "green",
                    "blue", "indigo", "violet" };

main()
{
  for ( colour = red; colour <= violet; colour++ )
  {
    printf ( "%s ", rainbow[colour]);
    }
  printf ( "\n" );
  }

 /* -------------------------------cut here--------------------------------- */

   The output of which is ( not surprisingly ):

red orange yellow green blue indigo violet 

   One quite advanced use of initialised arrays and pointers is the jump or
   dispatch table. This is a efficient use of pointers and provides a very much
   better ( In my opinion ) method of controlling program flow than a maze
   of case or ( heaven forbid ) if ( ... ) goto statements.

   Please cut out this program, read and compile it.

 /* -------------------------------cut here--------------------------------- */

char *ident = "@(#) tellme.c - An example of using a pointer to a function.";

#include <stdio.h>      
#include <math.h>
#include <sys/errno.h>

/*
These declarations are not in fact needed as they are all declared extern in
math.h. However if you were to use routines which are not in a library and
therefore not declared in a '.h' file you should declare them. Remember you
MUST declare external routines which return a type other than the int type.

extern double  sin ();
extern double  cos ();
extern double  tan ();
extern double atof ();
*/

struct table_entry
{
  char *name;                        /* The address of the character string. */
  double (*function)();   /* The address of the entry point of the function. */
  };

typedef struct table_entry TABLE;

double help ( tp )
TABLE *tp;
{ printf ( "Choose one of these functions:- " );
  fflush ( stdout );
  for ( ; tp -> name; tp++ ) printf ( "%s ", tp -> name );
  printf ( "\nRemember the input is expressed in Radians\n" );
  exit ( 0 );
  return ( 0.0 );  /* Needed to keep some nit-picking dumb compilers happy! */ 
  }

/*
** This is the array of pointers to the strings and function entry points.
** Is is initialised at linking time. You may add as many functions as you 
** like in here PROVIDED you declare them to be extern, either in some .h 
** file or explicitly.
*/

TABLE interpretation_table [ ] =  
{
  { "sin",  sin  },
  { "tan",  tan  },
  { "cos",  cos  },
  { "help", help },
  {  NULL,  NULL }               /* To flag the end of the table. */
  };

char *output_format = { "\n %s %s = %g\n" };
extern int errno;
extern void perror();

main( argc, argv )
int argc;
char **argv;
{
  TABLE *tp;
  double x, answer;

  if ( argc > 3 )
  {
    errno = E2BIG;
    perror ( "tellme" );
    exit ( -1 );
    }

  for (;;)                  /* This is the way to set up a continuous loop. */
  { 
    for ( tp = interpretation_table;
          ( tp -> name && strcmp ( tp -> name, argv[1] )); 
          tp++
          ) ;                 /* Note use of empty for loop to position tp. */

    if ( tp -> function == help ) (*tp -> function )( interpretation_table );

    if ( tp -> name == NULL )
    { 
      printf ( "Function %s not implemented yet\n", argv[1] );
      exit ( 1 );
      }
    break;                     /* Leave the loop. */
    }

  x = atof ( argv[2] );        /* Convert the character string to a double. */
  answer = ( *tp -> function )( x );/* Execute the desired function.        */
  printf ( output_format,      /* Pointer to printf()'s format string.      */
           argv[1],            /* Pointer to the name of the function.      */
           argv[2],            /* Pointer to the input number ascii string. */
           answer              /* Value ( in double floating point binary ) */
           );
  }

 /* -------------------------------cut here--------------------------------- */

Copyright notice:-

(c) 1993 Christopher Sawtell.

I assert the right to be known as the author, and owner of the
intellectual property rights of all the files in this material,
except for the quoted examples which have their individual
copyright notices. Permission is granted for onward copying,
and storage in electronic data retrieval systems, but not
modification, of this course and its use for personal
study only, provided all the copyright notices are left in the 
text and are printed in full on any subsequent paper reproduction.
In other words you may pass it around to your friends and print it
out in full on paper, but you may not steal my text and pretend
you wrote it, change the text in any way, or print it as a bound book.

--
 +----------------------------------------------------------------------+
 | NAME   Christopher Sawtell                                           |
 | SMAIL  215 Ollivier's Road, Linwood, Christchurch, 8001. New Zealand.|
 | EMAIL  chris@gerty.equinox.gen.nz                                    |
 | PHONE  +64-3-389-3200   ( gmt +13 - your discretion is requested )   |
 +----------------------------------------------------------------------+
