Command Line Arguments and Makefiles in C
Function Pointers in C
CSE 2421
Required Reading: Pointers On C, Chapter 13, Sections 13.2 through 13.3
Pointers – Review
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);
Declaration of pointer as return type
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);
How about this one?
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???
Function pointer declaration
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.
Using function pointers
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;
Type of a function pointer
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.
Type of a function pointer cont.
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).
Note on assignment to function pointers
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).
Note on assignment to function pointers
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.
How do we call a function with a function pointer?
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)();
NOTE: All of these calls explicitly dereference the function pointer. Again, the parentheses are necessary!
We can also make these calls as shown on the next slide, without dereferencing.
Function call using a function pointer without dereferencing (more usual)
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();
12
Why use function pointers?
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
Arrays of function pointers
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.
Replacing switch/if statements
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 */
Replacing switch/if statements
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 */
Using function pointers instead
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[4])(float, float);
fp[0] = add; /* make fp[0] point to add function */
fp[1] = subtract; /* make fp[1] point to subtract function */
fp[2] = multiply; /* make fp[2] point to multiply function */
fp[3] = divide; /* make fp[3] point to divide function */
Using function pointers instead
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)(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);
if ( (option > 0) && (option <= 4)) {
return ( fp[option – 1](a, b));
}
else printf(“Invalid choice entered. Must be 1, 2, 3, or 4!\n”);
}
Note: if ( (option > 0) && (option <= 4)) could have been written as if((unsigned)option <= 4) why??
Is that a better option??
Using function pointers instead
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) {return (a – b);}
int main () {
float result;
float (*fp[4]) (float, float) = {add, subtract, multiply, divide};
result = math_op(fp);
printf(“The result is: %f\n”, result);
return 0;
}
/docProps/thumbnail.jpeg