int i, *pointer; /* "pointer" is a variable for storing pointer to int */ pointer = &i; /* "pointer" assigned the address of i */ *pointer = 2; /* "int" pointed at by "pointer" is assigned the value 2 */ j = 2 * *pointer; /* set "j" to twice the value pointed at by "pointer" */
The (monadic) operator "&" is "address of", while "*" is "object pointed at by". The variable name is "pointer" (no star).
Pointers are typed by the object they point at; you can't assign the address of a long int into a pointer to an int.
When you declare a pointer, it is not initialised to point at anything, unless you set it. To declare
int *point;
and to then use
*point = ....
will generate an error. You must first initialise the pointer with
point = &variable;
or initialise it in the declaration with
int total = 0; int *point = &total;
is OK.
The notation for an initialised pointer declaration
int *point = &total;
is perfectly acceptable with the meaning of
int *point; point = &total;
but can be confusing to read. In the abbreviated version, we are declaring "*point" but assigning to "point". Although it is written "*point\ =\ ..." it actually means "point\ =\ ...".
int array[ 10 ], *arr_pt; arr_pt = &array[ 0 ]; arr_pt = array; /* identical, since "array" is pointer to "array[0]" */ /* *arr_pt is now equivalent to array[0]. */ arr_pt++; /* increment arr_pt to point at next object */ *arr_pt = 2; /* assigns to array[1] */ *( arr_pt + 3 ) = 2; /* assigns to array[4] */
In general "<pointer> plus-or-minus <integer>" gives a pointer; and "<pointer> minus <pointer>" gives an integer.
To sum the elements of an initialised array, use a terminating zero.
int array[] = { 1, 2, 3, 5, 7, 11, 0 }; int *point, sum = 0; for( point = array; *point != 0 ; point++ ) { /* the "*point" means "*point != 0" */ sum += *point; } printf( "total %d\n", sum );
To count characters in an array of characters terminated by a '.' use:
char buffer[ 100 ], *pc; int e_count = 0; for( pc = buffer; *pc != '.'; pc++ ) { if ( *pc == 'e' ) { e_count++; } }
The expression "*p++" is interpreted as "*(p++)", i.e. increment "p", deliver what it previously pointed at.
Whereas "(*p)++" increments whatever "p" points at. Given
char c, *p, a[10]; p = a; /* point p at a[0] */
then
c = *p++;
is equivalent to
{ c = *p; p++; } /* c is a[0], p points at a[1] */
but
c = (*p)++;
is equivalent to
{ c = a[0]; a[0]++; } /* p still points at a[0] */
We will give two simple example programs, each in two forms, firstly using subscripts in the arrays, then using pointers.
To read two arrays of floats, and form their scalar product.
/* scalar product */ float a[10], b[10]; float scalar_prod = 0.0; main() { int i; for ( i = 0; i < 10; i++ ) { scanf( "%f", &a[i] ); } for ( i = 0; i < 10; i++ ) { scanf( "%f", &b[i] ); } for ( i = 0; i < 10; i++ ) { scalar_prod += a[i] * b[i]; } printf( "Scalar prod %5.1f\n", scalar_prod ); }
/* scalar product */ float a[10], b[10]; float scalar_prod = 0.0; main() { int i; float *pa, *pb; for ( i = 0, pa = a; i < 10; i++ ) { scanf( "%f", pa++ ); } for ( i = 0, pb = b; i < 10; i++ ) { scanf( "%f", pb++ ); } for ( i = 0, pa = a, pb = b; i < 10; i++ ) { scalar_prod += *pa++ * *pb++; } printf( "Scalar prod %5.1f\n", scalar_prod ); }
To print the first few lines of a Pascal triangle.
/* Print Pascal's triangle */ #define MAX 100 int old[ MAX ], new[ MAX ], i, l; int n = 5; main () { old[0] = 1; for ( l = 1; l < n; l++ ) { new[0] = 1; for ( i = 1; i <= l; i++ ) { new[i] = old[i-1] + old[i]; } for ( i = 0; i <= l; i++ ) { old[i] = new[i]; printf ( "%4d", new[ i ] ); } printf ( "\n" ); } /* for l = 0 to n */ }
/* Print Pascal's triangle */ #define MAX 6 int old[ MAX ], new[ MAX ], l; main () { int *pold, *pnew; *old = 1; for ( l = 1; l < MAX - 1; l++ ) { *new = 1; for ( pold = old+1, pnew = new+1; *pnew++ = *(pold-1) + *pold; pold++ ) { ; } for ( pold = old, pnew = new; *pnew; ) { printf ( "%4d", *pnew ); *pold++ = *pnew++; } printf ( "\n" ); } /* for l = 0 to n */ }
We will assume that we are given an int variable "i" and an array "a" of 9 ints
int i, a[9];
We then declare an array "pa" of 3 pointers to ints, either as
int *pa[] = { a, a + 3, a + 6 };
or exactly equivalent
int *pa[] = { &a[0], &a[3], &a[6] };
We then declare a single pointer to pointer to int, either as
int **ppa; ppa = pa;
or exactly equivalent
int **ppa; ppa = &pa[0];
Now add two more declarations of general point to int and pointer to pointer to int.
int *pi = a; /* i.e. pi = a */ int **ppi = ppa;
The above data can be represented pictorially as follows.
We can now get the value of "pa[0]" by either "*ppa" or "*ppi".
Using double pointers, we can refer to "a[0]" as "**ppa" or "**ppi" or "*pa[0]".
If we execute
ppi++; pi++;
we get the revised picture
Note the importance of order of evaluation in
pi = *ppi++;
which is equivalent to
pi = *ppi; ppi++;
and
i = *++pi;
which is equivalent to
pi++; i = *pi;
and
pi = (*ppi)++;
which is equivalent to
pi = pa[1]; pa[1]++;
Another way of obtaining a structure similar to the above is as follows.
int a0[] = { 1, 4, 2, 6, 0 }; int a1[] = { 9, 3, 0 }; int a2[] = { 7, 6, 5, 4, 3, 2, 0 }; int *t[] = { a0, a1, a2, 0 }; /* t can be used like a [3][?] array */
int *p, **q; p = t[1]; /* p points to the start of a1 */ q = t; /* q points to value of a0 */
*p = ... /* *p is a1[0] */ **q = ... /* *q is t[0] or a0 */ /* **q is a0[0] */
If we execute
p++; /* *p is now a1[1] */ .. = *q++; /* delivers t[0] */ /* but *q is now t[1] */ /* **q is now a1[0] */
/* *p is now a1[1] */ *(p+2) is now a1[3] */ *(*q++)++; /* **q is now t[2][1] */
A typical for loop might now be:
for( q = t; *q != 0; q++ ) /* scan q through t */ for( p = *q; *p != 0; p++ ) /* scan *p over elements of t */ ...
Strings are handled specially in C.
The denotation "eric" (including the quote signs) is valid anywhere (not just in global), and delivers a pointer to a preassigned string of characters
{ 'e', 'r', 'i', 'c', 0 }
Note the terminating zero. Thus we can have
char *name; name = "eric";
or
char *name = "eric";
Because of the terminating zero convention we can write
char *p; for( p = name; *p; p++ ) { ... *p ... }
All strings in C are assumed to be character arrays which end with a null, for example in
printf( "His name is %s", name );
the variable "name" must be a character pointer, and characters are printed until a null is found.
[What's the difference between "printf(\ name\ )" and "printf(\ "%s",\ name\ )"?]
To copy the string pointed at by "q" into the area pointed at by "p" (they must both be of type "char *"), we use
while ( *p++ = *q++ ) ;
To compare two strings (until a null is encountered) pointed at by p and q
while( *p ) if ( *p++ != *q++ ) return 0; /* return 0 is FALSE return */ if ( *q ) return 0; /* if not at end of string "q" */ return 1; /* return 1 is TRUE exit */
The library functions for strings include
strcmp(a, b ) /* compare two strings */ strcpy(a, b ) /* copy b to a */ strlen(a) /* deliver the length */ strcat(a, b ) /* concatenate b onto end of a */ strncmp(a, b, n) /* compare at most n characters */ etc
See the on-line manual "man string" for further details.
The call
system( "who" );
causes the program to be suspended while the command "who" is executed (with its standard input and output connected to the terminal), after which program execution continues. The "system" call actually calls a shell to interpret the string.
system( "a.out" ); /* infinitely recursive, nasty! */
printf( "The date is " ); system( "date" );
system( "cc prog.c; a.out < data; rm a.out" );
system( "rm *.o" );
strcpy( p, "ed " ); strcat( p, file ); system( p ); /* edit the named file */
system( "stty cbreak" ); ... system( "stty -cbreak" );
system( "cd /tmp; ....; ...." );
Arrays of strings (of assorted lengths) are often necessary, in looking up command names, people's names, names of months, ... They are declared as
char *months[] = { "January", "February", "March", "April", "May", "June", 0 };
This sets up an array of pointers to characters, with a null pointer at the end of each string and at the end of the array of pointers. Now add
char **q, *p: q = months; /* could be "q = &months[0];" */ p = *q;
We can do the following:
/* print the names */ q = months; while( *q ) { printf( "%s\n", *q++ ); }
/* look for a name */ while( *q && strcmp( "April", *q++ ) ) { ; } if ( *q == 0 ) { /* not found */ ... } else { { q--; /* *q is the one found */ ... } }
q = months; /* *q points to "January", **q is 'J' */ q++; /* *q points to "February", **q is 'F' */ (*q)++; /* *q points to "ebruary", **q is 'e' */
The same as printf and scanf, but an extra first parameter, a "char *", used instead of actual i/o.
sprintf( s, "value is %d", rate ); /* sets in s "value is 123" */ /* s must point at a large enough space */ sscanf( s, "%d", &i ); /* looks for a decimal value in s */
sprintf( s, "cc %s.c", progname ); system( s );
When a command (calling a program) is issued with parameters, as in
a.out this that other
the system generates an integer argc counting the arguments (four in this case) and an object argv set up to represent the command line, as in
int argc = 4; char *argv[] = { "a.out", "this", "that", "other", 0 };
("argc" means argument count, "argv" means argument values.) Both of these data items can be picked up from the main program.
Use
main( argc, argv ) int argc; char *argv[]; { ... etc
The parameter "argv" is the assembled array of strings (including the command name, "a.out" in this case), while "argc" is the number of (non-null) entries. ("argc" is not essential, since its value could be found by scanning through "argv"; it is just convenient; you must include it.) The data in the variables is set up by the system.
if ( argc > 1 ) { printf( "arg 1 is %s\n", argv[1] ); } printf( "Program is called %s\n", argv[0] );
To print out the arguments (which is what the "echo" command does), the program would be
main( argc, argv ) int argc; char *argv[]; { int i; for ( i = 1; i < argc; i++ ) printf( "Arg no %d is %s\n", i, argv[ i ] ); }
or, with a more pointer-oriented method,
main( argc, argv ) int argc; char *argv[]; { argv++; while ( *argv ) { printf( "Next arg is %s\n", *argv++ ); } }
To work out the code to pick up the flags in "cc -s -O prog.c", we first look at the "argv" data as set up by the system.
The code to look for flags would be:
while( argc > 1 && argv[1][0] == '-' ) { switch( argv[1][1] ) { case 's' : strip = 1; break; case 'O' : optimise = 1; break; default : printf( "Don't recognise ..." ); } argc--; argv++; }
You could, of course, do all this with pointers instead of subscripts.
After a single "argc--; argv++;" the picture becomes
After the loop is finished we have the picture
The above code handles separate flags such as "cc\ -s\ -c\ prog". If the flags occur several together, as in "ls\ -lrt" use
if( argc>1 && argv[1][0] == '-' ) { i = 1; while( c = argv[1][i++] ) switch( c ) ... as above
To read numeric values from arguments, use the "atoi" (ASCII to integer) function as follows.
if ( argc > 2 ) { rate = atoi( argv[1] ); hours = atoi( argv[2] ); }
or
argv++; argc--; if ( argc > 0 ) { rate = atoi( *argv++ ); argc--; } if ( argc > 0 ) { hours = atoi( *argv++ ); argc--; }
A third parameter of the same type as "argv" can be used to pick up your UNIX environment ...
main( argc, argv, envp ) int argc; char *argv[], *envp[];
where "envp" contains pointers to strings such as "USER=ef", "TERM=vt100", and so on, and is null terminated. The library function "getenv(\ "USER"\ )" then delivers a pointer to the string following "USER=" if it can find one starting "USER=".
Pointers can be used with structures, using all the techniques explained above. There is one significant extension relating to the operator "->".
struct date toady, week[7], *p; p = &today; p = week; (*p).name = "Mon"; p -> name = "Mon"; p++; p = week + 6; p = (struct date *) 0177756; /* cast */
What is meant to look like an arrow is formed of a '-' and a '>'. It is preceded by a pointer to a structure, and followed by a fieldname.
struct cell { int data; struct cell *next; }; for( p = head; p != NULL; p = p->next ) { .... }
struct disc { int drive, sector, block; char error, mode; unsigned data; }; struct disc *d; d = (struct disc *) 01777756; if ( d->error < 0 ) ... d->sector = ...
Copyright Eric Foxley 1996
Notes converted from troff to HTML by an Eric Foxley shell script, email errors to me!