Pointers are not included in this course, but you are welcome to read these notes. There are no examples or exercises for this unit. Pointers are dealt with fully in the next C++ course.
Pointers are for making direct reference to stored objects, without necessarily having to refer to them by identifier.
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
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 (unpredictable) error. If you initialise the value by
int total; int *point = &total; *point = ....
then all 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. 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; point++ ) { // the "*point" means "*point != 0" sum += *point; } // for point in array cout << "total << sum << "\n";
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_count' ) { e_count++; } } // for pc in buffer
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];
then
p = a; // point p at a[0] c = *p++;
is equivalent to
{ c = *p; p++; } // now c is a[0], p points at a[1]
but
p = a; // point p at a[0] c = (*p)++;
is equivalent to
{ c = a[0]; a[0]++; } // now 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, first using subscripts:
// scalar product main() { float a[10], b[10]; float scalar_prod = 0.0; int i; for ( i = 0; i < 10; i++ ) { cin >> a[i]; } for ( i = 0; i < 10; i++ ) { cin >> b[i]; } for ( i = 0; i < 10; i++ ) { scalar_prod += a[i] * b[i]; } cout << "Scalar prod " << scalar_prod << "\n"; }
Now using pointers:
main() { float a[10], b[10]; float scalar_prod = 0.0; int i; float *pa, *pb; for ( i = 0, pa = a; i < 10; i++ ) { cin >> *pa++; } for ( i = 0, pb = b; i < 10; i++ ) { cin >> *pb++; } for ( i = 0, pa = a, pb = b; i < 10; i++ ) { scalar_prod += *pa++ * *pb++; } cout << "Scalar prod " << scalar_prod << "\n"; }
To print the first few lines of a Pascal triangle, first using subscripts:
// Print Pascal's triangle const int MAX = 100; main () { int old[ MAX ], new[ MAX ], i, l; int n = 5; 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]; cout << new[ i ] << " "; } cout << "\n"; } // for l = 0 to n } // end main
Then using pointers:
const int MAX = 6; main () { int old[ MAX ], new[ MAX ], l; 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; ) { cout << *pnew << " "; *pold++ = *pnew++; } cout << "\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 pointers 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 with single pointers as "*pa[0]" or "*pi".
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]
*(*q++)++; // **q is now t[2][1]
A typical for loop might now be:
for( q = t; *q; q++ ) { // scan q through t for( p = *q; *p; p++ ) { // scan *p over elements of t ... } // p loop } // q loop
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!
cout << "The date is "; system( "date" );
system( "g++ prog.C; a.out < data_file; 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; p = *q;
We can do the following:
// print the names q = months; while( *q ) { cout << *q++, "\n"; }
// 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 arguments, as in
a.out this that other
the system generates an integer \mIargc\m-I counting the arguments (four in this case) and an object \mIargv\m-I 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( 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 an integer, 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.
To print out the arguments (which is what the "echo" command does), the program would be
main( int argc, char *argv[] ) { int i; for ( i = 1; i < argc; i++ ) { cout << "Arg no " << i << " is " << argv[i] << "\n"; i, argv[ i ] ); } // end for i } // end main
or, with a more pointer-oriented method,
main( int argc, char *argv[] ) { argv++; while ( *argv ) { cout << "Next arg is " << *argv++ << "\n"; } // end while } // end main
To work out the code to pick up the flags in "g++ -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 : cerr << "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 "g++\ -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( int argc, char *argv[], char *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 "->".
date today, 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; }; 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!