指针是内存的地址。
函数指针指向函数代码开始的内存地址。
通常,您在编译时就知道要调用什么函数,因此您可以通过名称来引用它。当代码 si 被编译时,编译器会将该名称替换为实际机器代码的(通常是相对的)地址。
但有时您不知道在编译时要调用什么函数。您可以使用开关或其他东西来调用不同的功能:
switch(c) {
case '+' : add( op1, op2); break;
case '*' : multiple( op1, op2); break;
case '/' : divide(op1, op2); break;
}
但这很乏味且容易出错。
或者您可能正在编写一个库函数,例如qsort,它知道如何对任何类型进行排序,但需要一个比较器函数来进行排序。这样我们就不必打开并重新编译 qsort,qsort 允许我们将我们希望它用来比较我们的用户定义类型的代码(即函数)传递给它。
因此,C(和 C++)允许获取该代码的地址——函数的地址——。取地址会产生一个指向函数的类型指针的值(实际上是指向函数类型的类型指针,其中函数的签名的类型——即类型以及它的参数数量——以及它的返回类型,它返回的对象的类型。(注意,签名不包括返回类型,而是该函数包括两个签名和返回类型。)
无论如何,这个产生的值当然可以分配给一个兼容类型的变量——一个指向函数的指针,并像任何其他变量一样传递或复制,因为它毕竟只是一个数字,一个内存中的地址。
假设我们有一个函数 foo,返回类型为 int,签名为 const void*, const void*:
int foo( const void* lhs, const void* rhs);
我们可以创建一个该类型的变量:
int ( *foopointer) ( const void*, const void* ) ;
是的,真的,这是一个变量声明!将其读作“foopointer 是一个指向具有两个 const void 指针并返回 int 的函数的指针”,通过读取名称(“foopointer”)然后向左到指针(“*”),然后 right到括号 ("const void*, const void*)",然后返回 left 到它返回的 int。第一组括号是必要的,以防止 this 声明返回指向 int 的指针的函数。
然后我们可以使用地址运算符(“&”)将函数 foo 的地址分配给变量 foopointer:
foopointer = &foo;
实际上,地址操作符并不是绝对必要的,如果我们不使用它,它是隐含的,但使用它来明确您正在获取地址。它可以帮助读者。
然后我们可以使用foopointer,比如说调用qsort:
int some_array_of_int[] = { 1, 3, 2 ) ;
qsort( some_array_of_int,
3 /* elements */,
sizeof(int) /*each element is how big?*/,
foopointer) ;
当然,我们可以直接使用 foo:
qsort(some_array_of_int,
3 /* 元素 */,
sizeof(int) /每个元素有多大?/,
富);
无论哪种方式,qsort 现在将使用foo 来比较我们数组中的数字,以便对它们进行排序。 (比较器函数应该返回特定的值以允许这种情况发生,这超出了本次讨论的范围,但一般来说,*lhs - *rhs)。
现在我们还没有指定 foo 是如何排序的,但是我们不必为了 reverse 它的排序方式,因为我们可以返回 foo 的否定返回值:
int reverse_foo( const void* lhs, const void* rhs) {
return - foo( lhs, rhs ) ;
}
现在我们可以在运行时决定是升序还是降序排序或列出:
bool reverse = get_reverse_from_user_or_somthing();
qsort( some_array_of_int,
3 /* elements */,
sizeof(int) /*each element is how big?*/,
reverse ? reverse_foo : foo ) ;
写 qsort 的人不知道你会如何写 foo(和 reverse_foo),而且你(可能)没有办法重新编译 qsort 让它知道 foo(或 @ 987654336@)。但是由于函数指针,qsort 可以调用 foo,尽管这两个函数是相隔多年编写的,由不同的编码人员编写。