OSU CSE 2421
Required Reading: Pointers On C, Chapter 13, Sections 13.2 through 13.3
J.E.Jones
OSU CSE 2421
We’ve seen pointers to simple types before, e.g.: int *ptr1;
float *ptr2;
char *ptr3;
We also saw examples of pointers to pointers (these can be used as pointers to arrays, for example):
float **sets_ptr; /* sets_ptr is a pointer to a pointer to float*/ What about the following? What does it declare?
int *findValue(int value, int size, const int *array);
J. E. Jones
OSU CSE 2421
int *findValue(int value, int size, const int *array);
This declares a function which returns a pointer It might do something such as the following:
/* This function is passed a value which may be an
* element of an array, and if the value is found in the * array, it returns a pointer to it. If the element is
* not found, it returns a null pointer.
*/
int *findValue(int value, int size, const int *array);
J. E. Jones
OSU CSE 2421
int (*func_p)();
What do you think this declares?
It’s clearly different from the declaration of a function that returns a pointer,
but what does it declare?
Compare it to this declaration:
int *func();
What’s the difference between the two???
J. E. Jones
OSU CSE 2421
int (*func_p)();
This declares a pointer to a function (or a function pointer)!
A pointer to a function makes sense, because a function consists of code and that code commences, or begins, at some address.
Therefore, we can declare a pointer which can be assigned an address (so we have just declared an 8-byte variable that will contain an address). When we initialize the variable it will contain a pointer that points to the beginning of the code for some function (the first instruction in the function).
Notice that the function pointer is a variable; that is, it can be assigned the addresses of different functions at different points in the program.
The parentheses around *func_p are necessary! Otherwise, the compiler will treat this as a declaration of a function which returns a pointer, and not a function pointer.
J. E. Jones
OSU CSE 2421
Just as a pointer to anything is not useful until it contains a valid address, a function pointer is not useful until it actually points to some function that we wish to call.
Example of assignment to function pointer:
/* Prototype of function which returns the max value in an array*/ int returnMax(int size, const int *array);
/* Declare fp, and make it point to the function returnMax */
int (*fp)(int, int*); fp = &returnMax;
or
int (*fp)(int, int*) = &returnMax;
J. E. Jones
OSU CSE 2421
The type of a function pointer includes
◦ the return type, and
◦ the number and types of any parameters, in a specific order.
For example, consider the declarations on the preceding slide: int returnMax(int size, const int *array);
int (*fp)(int, int*) = &returnMax;
This declares fp to be a pointer to a function which returns an int, and which has two parameters, the first an int, and the second an int pointer.
Because the function returnMax returns an int, and has two parameters, the first an int, and the second an int pointer, the address of returnMax can be assigned to fp.
J. E. Jones
OSU CSE 2421
long returnLow( int lower, int upper, char *values); int (*fp)(int, int*);
fp= &returnLow;
It would not be valid to assign the address of a function which has a return type other than int to fp.
Neither would it be valid to assign the address of a function which has a different number of parameters, or which has two parameters, but of different types (that is, the first one not an int, or the second one not an int pointer).
J. E. Jones
OSU CSE 2421
NOTE: We do not have to use the address operator, &, when we assign the address of a function to a function pointer, but we can.
Therefore, after the declaration of returnMax, the following two assignments to fp are equivalent:
int returnMax(int size, const int *array); int (*fp)(int, int*) = returnMax;
int (*fp)(int, int*) = &returnMax;
This is because the compiler always converts the function name to a function pointer (an address) before using it (You can compare this to the name of a variable which is an array, which the compiler also treats as a pointer or address).
J. E. Jones
OSU CSE 2421
The initialization can also be accomplished with a separate assignment statement:
int returnMax(int size, const int *array);
int (*fp)(int, int*);
fp = returnMax; /* OR fp = &returnMax; */
Notice that function returnMax must be declared (but not necessarily defined) before assigning its address to the function pointer. (i.e. at least, the prototype for returnMax must be present.)
This is so that the compiler can do checking for type compatibility.
The Linker/Loader will assign the final address, likely when the final executable loads in to main memory to run.
J. E. Jones
OSU CSE 2421
For the declarations on the previous slides, the call would be: int i;
i = (*fp)(argument1, argument2);
If the function to which fp points has no parameters, the call would be: i = (*fp)();
If the function to which fp points has no parameters, and no return value, the call would be:
(*fp)();
All of these calls explicitly dereference the function
NOTE:
pointer. Again, the parentheses are necessary!
We can also make these calls as shown on the next slide, without dereferencing.
J. E. Jones
OSU CSE 2421
Without dereferencing fp: int i;
i = fp(argument1, argument2);
If the function to which fp points has no parameters, the call without dereferencing would be:
i = fp();
If the function to which fp points has no parameters, and no return value, the call without dereferencing would be:
fp();
J. E. Jones
OSU CSE 2421
Function pointers provide some extremely interesting, efficient and elegant programming techniques, though they are not very common in C programs.
You can use them to:
– Replace switch/if statements,
– Implement callbacks*
– Realize your own run-time-binding*
Which leads to:
Greater flexibility and better code reuse.
*beyond the scope of CSE 2421
J. E. Jones
OSU CSE 2421
C treats pointers to functions just like pointers to data; so, we can have an array of pointers to functions.
This offers the possibility of selecting a function using an index, and this can be used to replace an if, or a switch statement when the functions all pass the same number and type of parameters.
For example:
Suppose we’re writing a program that displays a menu of commands for the user to choose from. We can write functions that implement these commands, and then store pointers to the functions in an array.
J. E. Jones
OSU CSE 2421
Suppose the following code: float math_op() { /* begin function math_op */
int option;
float a, b;
printf(“Enter 1 to add, 2 to subtract, 3 to multiply, or 4 to divide:\n”); scanf(“%d”, &option);
printf(“Enter the two operands:\n”);
scanf(“%f %f”, &a, &b);
/* code continued on next slide */
J. E. Jones
OSU CSE 2421
switch (option) {
case 1: return (a + b);
break;
case 2: return (a – b); break;
case 3: return (a * b); break;
case 4: if (b!=0) return (a / b) else return(0); break;
default: printf(“Invalid choice entered. Must be 1, 2, 3, or 4!\n”);
} /* end switch */
} /* end function math_op */
J. E. Jones
OSU CSE 2421
First, define four functions to perform the four operations, add, subtract, multiply and divide, each returning a float, and taking two float parameters;
float add(float a, float b) {return (a + b);}
float subtract(float a, float b) {return (a – b);}
float multiply(float a, float b) {return (a * b);}
float divide(float a, float b) { if (b!=0) return (a / b) else return(0);}
Then, declare an array of four function pointers, and assign addresses of the functions:
float (*fp_array[4])(float, float);
fp_array[0] = add; fp_array[1] = subtract; fp_array[2] = multiply; fp_array[3] = divide;
/* fp_array[0] points to add(a,b) */ /* fp_array[1] points to subtract(a,b) */ /* fp_array[2] points to multiply(a,b) */ /* fp_array[3] point to divide(a,b) */
J. E. Jones
OSU CSE 2421
Now, rewrite function math_op as follows (Notice a pointer to the array of function pointers is passed as a parameter to the function):
float math_op(float (**fp_array)(float, float)) {
int option;
float a, b;
printf(“Enter 1 to add, 2 to subtract, 3 to multiply, or 4 to divide:\n”); scanf(“%d”, &option);
printf(“Enter the two operands:\n”); scanf(“%f %f”, &a, &b);
option -=1;
if ( (option >= 0) && (option <= 3)) {
return ( fp_array[option](a, b)); }
else printf(“Invalid choice entered. Must be 1, 2, 3, or 4!\n”); }
Note: if ( (option >= 0) && (option <= 3)) could have been written as if((unsigned)option <= 3) why?? Is that a better option??
J. E. Jones
OSU CSE 2421
Notice how the pointer to the array of function pointers is passed as a parameter to math_op()
#include
float add(float a, float b) {return (a + b);}
float subtract(float a, float b) {return (a – b);}
float multiply(float a, float b) {return (a * b);}
float divide(float a, float b) {if (b!=0)return (a/b) else return(0);}
int main () {
float result;
float (*fp_array[4]) (float, float) = {add, subtract, multiply, divide}; result = math_op(fp_array);
printf(“The result is: %f\n”, result);
return 0;
}
J. E. Jones