Previous: 14.2 Dynamic Memory Allocation
Up: 14 Storage Class and Scope
Next: 14.4 Summary
Previous Page: 14.2.2 Dynamic Arrays
Next Page: 14.3.1 Function Pointers as Returned Values

14.3 Pointers to Functions

We saw earlier that functions have a storage class and scope, similar to variables. In C, it is also possible to define and use function pointers, i.e. pointer variables which point to functions. Function pointers can be declared, assigned values and then used to access the functions they point to. Function pointers are declared as follows:

int (*fp)();
     double (*fptr)();
Here, fp is declared as a pointer to a function that returns int type, and fptr is a pointer to a function that returns double. The interpretation is as follows for the first declaration: the dereferenced value of fp, i.e. (*fp) followed by () indicates a function, which returns integer type. The parentheses are essential in the declarations. The declaration without the parentheses:
int *fp();
declares a function fp that returns an integer pointer.

We can assign values to function pointer variables by making use of the fact that, in C, the name of a function, used in an expression by itself, is a pointer to that function. For example, if isquare() and square() are declared as follows:

int isquare(int n);
     double square(double x);
the names of these functions, isquare and square, are pointers to those functions. We can assign them to pointer variables:
fp = isquare;
     fptr = square;

The functions can now be accessed, i.e. called, by dereferencing the function pointers:

m = (*fp)(n);       /* calls isquare() with n as argument */
     y = (*fptr)(x);     /* calls square() with x as argument */

Function pointers can be passed as parameters in function calls and can be returned as function values. Use of function pointers as parameters makes for flexible functions and programs. An example will illustrate the approach. Suppose we wish to sum integers in a specified range from x to y. We can easily implement a function to do so:

/*   File: sumutil.h */
     int sum_int(int x, int y);

/* File: sumutil.c */ #include <stdio.h> #include "sumutil.h" /* Function sums integers from x to y. */ int sum_int(int x, int y) { int i, cumsum = 0;

for (i = x; i <= y; i++) cumsum += i; return cumsum; }

The file sumutil.h contains prototypes for all the functions written in sumutil.c. Next, suppose we wish to sum squares of integers from x to y. We must write another function to do so:
/*   File: sumutil.h - continued */
     int sum_squares(int x, int y);
     int isquare(int x);

/* File: sumutil.c - continued */ /* Function sums squares of integers form x to y. */ int sum_squares(int x, int y) { int i, cumsum = 0;

for (i = x; i <= y; i++) cumsum += isquare(i); return cumsum; }

/* Function returns the square of x. */ int isquare(int x) { return x * x; }

Function isquare() returns the integer square of i. The constructions of the two functions sum_int() and sum_squares() are identical. In both cases, we cumulatively add either the integers themselves or squares of the integers. A function iself(), which returns the value of the integer argument, can be used in sum_int() to make the functions truly identical. Here is a modified function that uses iself():
/*   File: sumutil.h - continued */
     int sum_integers(int x, int y);
     int iself(int x);

/* File: sumutil.c - continued */ /* Function sums integers from x to y. */ int sum_integers(int x, int y) { int i, cumsum = 0;

for (i = x; i <= y; i++) cumsum += iself(i); return cumsum; }

/* Function returns the argument x. */ int iself(int x) { return x; }

The two sum functions, sum_integers() and sum_squares(), are now identical except for the functions used in the cumulative sum expressions. In one case, we use iself(), in the other case, isquare(). It is clear that a single more flexible generic sum function can be written by passing a function pointer, fp, as an argument with a value pointing to the appropriate function to use. The cumulative sum expression would then take the form:
cumsum += (*fp)(i);
Here is the implementation:
/*   File: sumutil.h - continued */
     int sum_gen(int (*fp)(), int x, int y);

/* File: sumutil.c - continued */ /* Function sums values of *fp applied to integers from x to y. */ int sum_gen(int (*fp)(), int x, int y) { int i, cumsum = 0;

for (i = x; i <= y; i++) cumsum += (*fp)(i); return cumsum; }

Finally, we can improve the generic sum function by using a pointer to a function that updates the integer using a specified step size:

/*   File: sumutil.h - continued */
     int sum(int (*fp)(), int x, int (*up)(), int step, int y);

/* File: sumutil.c - continued */ /* Function returns the sum of function *fp applied to integers from x to y, incremented by *up in step size. */ int sum(int (*fp)(), int x, int (*up)(), int step, int y) { int i, cumsum = 0;

for (i = x; i <= y; i = (*up)(i, step)) cumsum += (*fp)(i); return cumsum; }

The function pointed to by (*up) takes two arguments, an integer to be updated and the step size. The generic function sum() can now be used to sum (*fp)(i) applied to integers i, which are updated by (*up)(i, step). The pointer variable, fp can point to any function that processes an integer and returns an integer. Similarly, up can point to any function that returns an updated integer value.

Let us now write a program that reads starting and ending integers as well as step size until EOF. For each set of data read, the program first computes and prints the sum of integers using sum_int, and sum of squares using sum_squares(). These two sums are in steps of one, since that is how the functions are written. Next, the program uses the above generic sum function sum() to compute sums of integers and squares in specified step sizes. Figure 14.17 shows the program.

The update function used is iincr(), which merely returns x plus the step size. The program source files, sums.c and sumutil.c, are compiled separately and linked together. A sample run of the program is shown below:

Sample Session:

For each set of input data, the output first shows sums of integers and squares in steps of one, and then in specified steps.



Previous: 14.2 Dynamic Memory Allocation
Up: 14 Storage Class and Scope
Next: 14.4 Summary
Previous Page: 14.2.2 Dynamic Arrays
Next Page: 14.3.1 Function Pointers as Returned Values

tep@wiliki.eng.hawaii.edu
Sat Sep 3 07:21:51 HST 1994