//example 1 int * pi;says that *pi is of type integer, therefore, pi is of type pointer to integer. Note that the * associates with (in a manner of speaking)with int, not with pi. so that
//example 2 int * pi, p2; //oops! p2 is an int, not a pointer to an inthas the effect of making pi a pointer, but p2 is an integer!
To avoid ambiguity in declaring pointers, we use type definitions to create
pointer types. So, instead of example 2, we code
//example 3 typedef int * int_ptr; int_ptr pi, p2; // now, p2 is a pointer
Adding or subtracting an int from a pointer is legal, and has the effect of causing the pointer to point to the next object of the same type as the pointer. So,
//example 4 int A[10]; int_ptr pA = A; //pA points to the zero element of the arraypA+1 is the address of the array element A[1]. In general, pA+i = &(A+i), and *(pA+i) = A[i].
When passing an array as a parameter to a function, the array name is demoted in the function to a pointer. So -
//example 5 int foo(int a[], int size); // prototype ... in some function int Array[10]; z = foo(Array, 10);inside function foo, a is treated as type pointer to integer.
int TwoDim [10][20]
creates 10 arrays. Those 10 arrays are themselves
arrays of size 20 int's. TwoDim[0] is a 20-element array. Initializing an array when it
is declared, therefore, requires some care.//example 6 int A[2][3] = { {1,2,3}, {4, 5,6} };The declaration of A is as 2 arrays, each of which is a 3-element array. Hence, the declaration shows A gets initialized with 2 elements.
The compiler will figure out the size of an initialized array for us, if we wish.
//example 7 char B[ ][2] = { {'x', 'y'}, {'z','W'}, {'?','!'} };creates B as an array with 3 elements, or an array of size [3][2].
(as a reminder from 201 days, you can get the number of elements in an array by this little
trick: int asize = sizeof A / sizeof A[0];
.)
//example 8 int apply( int (*fp)(int), int x) { return (*fp)(x); }the function "apply" takes 2 arguments. The first argument is a pointer to a function that takes an integer and returns an integer. The second arg. is an integer, and function apply just invokes the function pointed to by fp with argument x.
As with example 3, we can create pointer-to-function types, e.g.
typedef int (*fp)(int);
to cause fp to assume type "pointer-to-function returning an int and taking 1 int".
This construct allows us to enforce a strong abstraction barrier between different abstractions.
For example, suppose that I were to write a function to build a table of values:
#include <iostream.h>
Note that the construction of the table does not depend on what function I'm putting in the
table. I might code a function
#include <iomanip.h>
void tabulate(char * header, fp fun, int low, int high, int step) {
cout << header << endl;
int x;
for (x = low; x <= high; x+=step)
cout <<setw(8) << x << setw(10) <<" " <<(*fun)(x);
}
int sq(int x) { return x*x;}
, or a function
int cube (int x){return x*x*x'}
. To print a table of cubes of multiples of
5 between 5 and 50, I just write: tabulate("cubes",cube, 5, 50, 5)
. To do
a table of squares of the same values, I code tabulate("squares",sq, 5, 50, 5)
.
(pretty useful!)
The problem in writing a general-purpose sorting routine is that C++ is strongly-typed, and when we try to pass an array of type T as a function parameter, we know that what gets passed is a pointer to an object of type T. It would be wonderful to instruct C++ to ignore the type, because we know what to do with the object, and we really do not care to invoke strong type checking just now (Hmmm. do you see the potential problem this creates?). We have such a mechanism, the void pointer, void *. Typecasting any pointer to type void * means:
void qsort( void * A, size_t nelem, size_t width, int (*comp)(void *, void *));(size_t is essentially an unsigned int)
An example would make these points more solid.
//example 9 int Itab[50]; int compare_ints(void * a, void * b) { return ( *(int *)a - *(int * ) b); } .... qsort(Itab, 50, sizeof(int), compare_ints); ...It is essential to note that the compare function, comnpare_ints, must conform to the prototype
int (*compare_function)(void *, void *)
for qsort to work correctly.
We can understand the body of compare_ints as follows: the function's 2 arguments are pointers to array elements, so we must dereference them to compare them. BUT, since these arguments are void pointer (void *), we must also typecast them to actual C++ data types or to programmer-devised types. The expression *(int*)a means just that. Since the typecast and dereferencing items are right-associative, (i.e. they associate from right to left), and the precedence of typecasts is the same as that of the dereference operator, the expression means "first, cast a to be a pointer to int, and dereference it."
You might think how to write a comparison function for strings (hint: use strcmp) or for this class, where the sorting order is last name, first name, middle initial.
class Student { friend int Compare_Student(void *, void *); private: char * FirstName; char * MiddleInit; char * LastName; ... more stuff public: ... even more stuff };