Function pointers¶
C makes some provisions for particular kinds of second-class values to be passed as function arguments by implicitly converting them to pointers. We’ve seen this in the case of arrays; strictly speaking, an array cannot be passed to a function in C, but using the name of an array as a value implicitly converts it to the array’s base address and pointers are first-class.
Functions in C are not first class. Function definitions cannot be
nested, variables cannot store function values, functions cannot be
assigned to or passed into or out of function calls. However, as with
arrays, using the name of a function as a value implicitly converts to
an address referring to the function (at the assembly level, this is
the address you would use with the call
instruction to jump to
the function’s first instruction).
Function pointer types are notorious for being somewhat verbose, since
the parameter and return types are included. For example, consider
the strcmp
function, which takes two strings as parameters,
compares them, and returns an integer indicating their relative order
(negative, zero, or positive, as the first argument is respectively
less than, equal to, or greater than the second argument). Its declaration
is this.
int strcmp(const char *s1, const char *s2);
The type corresponding to the address of strcmp
is this.
int (*)(const char *, const char *)
In this code, a function pointer named cmp
is declared and initialized
to point at strcmp
.
int (*cmp)(const char *, const char *) = strcmp;
Because such types get lengthy, they are very common candidates for typedef
.
If string comparison functions were going to come up much in a program, it is likely
that something like this would be used instead of the previous version.
typedef int (*cmp_t)(const char *, const char *);
cmp_t cmp = strcmp;
There are other functions with the same parameter and return types, such as
strcasecmp
, which compares its arguments case-insensitively. Function
pointers are often used to implement run-time polymorphism or a limited form
of functional programming, as in the following example of a function which
returns its minimum argument but allows the caller to specify what comparison
function to use.
char *minstr(char *s1, char *s2, cmp_t cmp)
{
return cmp(s1, s2) > 0 ? s2 : s1
}
This program demonstrates using function pointers to make a generic
accumulate
function which can be used to calculate sums and
products of arrays of integers.
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
typedef int (*op_t)(int, int);
int accumulate(int *array, int size, int value, op_t op)
{
int i;
for (i = 0; i < size; i++)
value = op(value, array[i]);
return value;
}
int main()
{
int nums[5] = {1, 2, 3, 4, 5};
printf("Sum = %d\n", accumulate(nums, 5, 0, add));
printf("Product = %d\n", accumulate(nums, 5, 1, multiply));
}