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 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 ouput 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 one can say:-
#define SIZE_OF_VERSE sizeof verse
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.
#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" );
}
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.
------------------------------------------------------------------------
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 )
*/
);
}
Lesson # 1, #
2, # 3, #
4, # 5, #
6, # 7, #
8
|