在C中,像正常的数据指针(int *,char *,等),我们也可使用指针指向函数。下面是一个例子声明和使用函数指针调用函数。

#include <stdio.h>
// 一个正常函数和一个int参数
// void返回类型
void fun(int a)
{
    printf("Value of a is %d\n", a);
}


int main()
{
    // fun_ptr 是一个指针指向function fun()
    void (*fun_ptr)(int) = &fun;


    /* 上面的一行等价下面的两行
    void (*fun_ptr)(int);
    fun_ptr = &fun;
    */


    // 使用fun_ptr调用fun()
    (*fun_ptr)(10);


    return 0;
}

输出:

Value of a is 10

为什么要给函数指针打括号,像fun_ptr?

如果我们去掉括号,那么表达式“void(*fun_ptr)(int)"变为”void *fun_ptr(int)",这是一个返回void类型指针的函数。

关于函数指针有一些有趣的事实。

1)不像正常指针,函数指针指向代码,不是数据。典型的,函数指针存储可执行代码的开始部分。

2)不像正常指针,不使用函数指针分配,释放内存。

3)函数名也可获得函数地址。例如,我们在赋值时去掉地址操作符'&'。也在函数调用时去掉*,程序依然工作。

 

#include <stdio.h>
// 一个正常函数和一个int参数
// void返回类型
void fun(int a)
{
    printf("Value of a is %d\n", a);
}


int main()
{
    void (*fun_ptr)(int) = fun;//&去掉


    fun_ptr(10);//去掉*


    return 0;
}

输出:

Value of a is 10

4)像正常指针,可以使用函数指针数组。下面第5点显示指针数组的语法。

5)函数指针可以代替switch语句。例如,用户被询问选择0到2做不同的任务。

#include <stdio.h>
void add(int a, int b)
{
     printf("Addition is %d\n", a+b);
}
void subtract(int a, int b)
{
     printf("Subtraction is %d\n", a-b);
}
void multiply(int a, int b)
{
     printf("Multiplication is %d\n", a*b);
}


 

int main()
{
     // fun_ptr_arr 是函数指针数组
     void (*fun_ptr_arr[])(int, int) = {add, subtract, multiply};
     unsigned int ch, a = 15, b = 10;

 

     printf("Enter Choice: 0 for add, 1 for subtract and 2 for multiply\n");
     scanf("%d", &ch);

 

     if (ch > 2) return 0;

 

     (*fun_ptr_arr[ch])(a, b);


     return 0;
}

Enter Choice: 0 for add, 1 for subtract and 2 for multiply
2
Multiplication is 150 

6)像正常的数据指针,函数指针可以当作参数传递,可以从函数返回。

例如,考虑下面的,wrapper()接收void()作为参数,调用传递的函数。

 

//函数指针做参数
#include <stdio.h>

//两个简单的函数
void fun1() { printf("Fun1\n"); }
void fun2() { printf("Fun2\n"); }

 

// 一个函数接受简单函数做参数然后调用这个函数
void wrapper(void (*fun)())
{
     fun();
}

 

int main()
{
     wrapper(fun1);
     wrapper(fun2);
     return 0;
}

这点很有用,我们可以使用函数指针避免代码冗余。例如,qsort()函数可以用来排序数组升,降或其它顺序。不仅此,使用函数指针和void指针,qsort用于任何数据类型都有可能。

 

//qsort and comparator
#include <stdio.h>
#include <stdlib.h>

// 用于升排序整型数组的comparator函数。
// 为了排列任意类型的数组或规则,我们只需要写更多的比较函数。并且我们可以使用同一个qsort()
int compare (const void * a, const void * b)
{
     return ( *(int*)a - *(int*)b );
}

 

int main ()
{
     int arr[] = {10, 5, 15, 12, 90, 80};
     int n = sizeof(arr)/sizeof(arr[0]), i;


     qsort (arr, n, sizeof(int), compare);

 

     for (i=0; i <n;i++)         printf ("%d ", arr[i]);
     return 0;
}

输出:

5 10 12 15 80 90

类似qsort(),我们可以写自己的函数用于任意数据类型,做不同的任务没有代码冗余。。下面是一个例子。实际上,我们可以使用这个查找函数查找相近函数(在一个阈值下)通过写自定义比较函数。

#include <stdio.h>
#include <stdbool.h>
// 用于查找整型数组的compare函数
bool compare (const void * a, const void * b)
{
     return ( *(int*)a == *(int*)b );
}
// 通用的search()函数可以用来查找arr_size大小arr[]中的元素*x
// void指针可以指任何类型
//  ele_size 是元素大小
int search(void *arr, int arr_size, int ele_size, void *x,
     bool compare (const void * , const void *))
{
     // 因为char固定占一个字节,我们可以用char指针指任何类型,为了指针正确,我们需要ele_size乘上index.

     char *ptr = (char *)arr;
     int i;
     for (i=0; i<arr_size;i++)          if (compare(ptr + i*ele_size, x))
               return i;
          // 如果元素没找到
     return -1;
}
int main()
{
     int arr[] = {2, 5, 7, 90, 70};
     int n = sizeof(arr)/sizeof(arr[0]);
     int x = 7;
     printf ("Returned index is %d ", search(arr, n,
                    sizeof(int), &x, compare));
     return 0;
}

输出:

Returned index is 2

上面的查找函数可以用于任意数据类型,通过写一个分开的自定义compare()函数。

7)许多C++面向对象的特点可以在C中用函数指针实现。例如虚函数。类方法是另一个使用函数指针实现的例子。

Reference:https://www.geeksforgeeks.org/function-pointer-in-c/