============================= CBE Pt. 17: Function Pointers [TOC] ============================= In short: Every function has an address. And this address can be store in an variable. Such a variable is then a pointer to a function. Of course this pointer variable should have the right type. In the following the declaration of function pointers is explained by example No Return Value, No Parameters ============================== This is the declaration of _foo_ which can be used to store the address of a function that does not accept any parameters and does not return a value: ---- CODE (type=c) ------------------------------------------------------------- void (*foo)(void); --------------------------------------------------------------------------------- Functions with Function Parameters ---------------------------------- And here some example where a function _dummy()_ that gets a function pointer as parameter and simply calls this function: ---- CODE (file=session20/func/func.c) ----------------------------------------- #include void dummy(void (*f)(void)) { f(); } // ------------------------ void foo(void) { printf("I am foo\n"); } void bar(void) { printf("I am bar\n"); } int main(void) { dummy(foo); dummy(bar); } -------------------------------------------------------------------------------- Here the output ---- SHELL (path=session20/func) ----------------------------------------------- gcc -Wall -o func func.c ./func -------------------------------------------------------------------------------- Since C89 the explicit usage of function pointer could be avoided and function _dummy()_ declared as ---- CODE (type=c) ------------------------------------------------------------- void dummy(void f(void)); -------------------------------------------------------------------------------- This is just an alternative notation but its underlying meaning and technically realization is the same. Hence, the example above can be rewritten as follows: ---- CODE (file=session20/func/func2.c) ---------------------------------------- #include void dummy(void f(void)) { f(); } // ------------------------ void foo(void) { printf("I am foo\n"); } void bar(void) { printf("I am bar\n"); } int main(void) { dummy(foo); dummy(bar); } -------------------------------------------------------------------------------- Here the output ---- SHELL (path=session20/func) ----------------------------------------------- gcc -Wall -o func2 func2.c ./func2 -------------------------------------------------------------------------------- Data Structures with Function Pointers -------------------------------------- Function pointers can be stored in data structures like arrays, lists, etc. With ---- CODE (type=c) ------------------------------------------------------------- void (*f[10])(void); -------------------------------------------------------------------------------- an array with 10 elements is declared. Each element is a pointer to a function. Here is became obvious to know the idea behind the notation for declaration in C: - In a statement the expression _f[i]_ has the type "pointer to a function with return type _void_ and no parameters" and - the expression _*f[i]_ has the type "function with return type _void_ and no parameters". In the following example an array of function pointers is used: ---- CODE (file=session20/func/func3.c) ---------------------------------------- #include #include void dummy(void f(void)) { f(); } // ------------------------ void foo(void) { printf("I am foo\n"); } void bar(void) { printf("I am bar\n"); } int main(void) { void (*f[])(void) = { foo, bar, }; for (size_t i = 0; i < sizeof(f) / sizeof(f[0]); ++i) { printf("calling f[%zu]: ", i); dummy(f[i]); } } -------------------------------------------------------------------------------- Here the output ---- SHELL (path=session20/func) ----------------------------------------------- gcc -Wall -o func3 func3.c ./func3 -------------------------------------------------------------------------------- Return Value and Parameters =========================== With ---- CODE (type=c) ------------------------------------------------------------- int (*foo)(int, int); --------------------------------------------------------------------------------- a function pointer _foo_ is declared for a function that returns a value of type _int_ and has two parameters of type _int_. So for parameters simply the identifiers are omitted compared to "a normal declaration". Hence with ---- CODE (type=c) ------------------------------------------------------------- int (*bar)(int, int (*)(int)); --------------------------------------------------------------------------------- or equivalently ---- CODE (type=c) ------------------------------------------------------------- int (*bar)(int, int(int)); --------------------------------------------------------------------------------- a function pointer _bar_ is declared to a function that returns a value of type _int_ and has two parameters. The first parameters is of type _int_ and the second a function pointer to a function with a return value of type _int_ and one parameter of type _int_. It is that simple ;-) Below an example with function pointers to functions that have a function pointer as parameter. It compares some simple rules for numerical integration (feel free to add more sophisticated methods): ---- CODE (file=session20/func/func4.c) ---------------------------------------- #include #include double f(double x) { return x * x; } double F(double x) { return x * x * x / 3; } // some simple rules for numerical integration double midpoint(double a, double b, size_t n, double f(double)) { double h = (b - a) / n; double res = 0; for (size_t i = 0; i < n; ++i) { res += f(a + (i + 0.5) * h); } return res * h; } double trapezoidal(double a, double b, size_t n, double f(double)) { double h = (b - a) / n; double res = f(a) + f(b); for (size_t i = 1; i < n; ++i) { res += 2 * f(a + i * h); } return res * h / 2; } // apply an integration rule to a given function void applyRule(const char *ruleName, double a, double b, size_t n, double f(double), double rule(double, double, size_t, double(double))) { printf("%s(%lf, %lf, %zu) = %lf\n", ruleName, a, b, n, rule(a, b, n, f)); } // print a report for comparing numerical results void compare(double a, double b, size_t n, double f(double)) { printf("compare:\n"); applyRule(" midpoint", a, b, n, f, midpoint); applyRule(" trapezoidal", a, b, n, f, trapezoidal); } int main(void) { printf("f(x) = x^2\n"); compare(1, 2, 5, f); compare(1, 2, 10, f); printf("exact result: %lf\n\n", F(2) - F(1)); printf("f(x) = sin(x)\n"); compare(1, 2, 5, sin); compare(1, 2, 10, sin); printf("exact result: %lf\n\n", -cos(2) + cos(1)); } -------------------------------------------------------------------------------- Here the output ---- SHELL (path=session20/func) ----------------------------------------------- gcc -Wall -o func4 -lm func4.c ./func4 --------------------------------------------------------------------------------