定义
函数指针就是指向函数的指针,其值就是函数体的首地址。在底层,函数名就代表函数的首地址,所以把函数名直接指派给一个同类型的函数指针而不需要&运算符,可以直接用函数名注册回调函数。
int(*fp)(const char *)=strlen;
调用的方式
有两种:
int len = fp("hehe"); // 这两种都可以
int len = (*fp)("hehe");
两段有意思的代码
看看下面这两个代码什么意思?出自<< C陷阱和C缺陷 >>
//代码一
(*(void(*)())0)();
//代码二
void(*signal(int, void(*)(int)))(int);
首先代码一 (*(void(*)())0)() 把(void(*)())0单独拎出来这是将int类型的0强制转换成void(*)()的一个函数指针类型,然后在解引用,最后对函数指针解引用后调用(),其实不解引用也可以调用结果一样。
我们可以借助typedef来简化:
// (*(void(*)())0)();
typedef void(*pFuc)();
(*(pFuc)0)(); // 代码一等价于这个形式
((pFuc)0)(); // 和上面的代码是一样的
再来看看代码二,void(*signal(int, void(*)(int)))(int); 把这个函数中signal(int, void(*)(int))单独拎出来,不难发现这是一个函数名为signal,形参为一个int类型和一个void(*)(int)的函数指针类型,然后外面的一层void(*___)(int)显然是修饰其返回值类型的,借助typedef来简化:
// void(*signal(int, void(*)(int)))(int);
typedef int(*pFuc)(int);
pfuc signal(int, pFuc);
应用场景
1. 回调函数:
调用者向系统或者框架注册一个函数指针,函数调用者不知道具体的调用时机,由操作系统或代码框架来决定。这样做能把有些特定的逻辑单独提取出来,让调用者插入一些自定制的代码。
这里给出一段改进版的冒泡排序,可以做到一份代码同时实现升序和降序两种功能:
#include <stdio.h>
typedef int(*Comp)(int, int);
void Swap(int*a, int*b) {
int t = *a;
*a = *b;
*b = t;
}
int Greater(int a, int b) {
return a > b ? 1 : 0;
}
int Less(int a, int b) {
return a > b ? 0 : 1;
}
void BubbleSort(int* arr, int size, Comp p) {
// [0,bound)已排序区间
// [bound,size)待排序区间
// 每次找一个最小值放最前面,bound边界也就++
int bound = 0;
while (bound < size) {
int sorted = 1; // 假设数组已经有序了
for (int cur = size - 1; cur > bound; --cur) {
if (p(arr[cur - 1], arr[cur])) {
Swap(&arr[cur - 1], &arr[cur]);
sorted = 0; // 发生过交换了,有序不成立
}
}
// 冒泡完,仍然有序
if (sorted == 1) {
return;
}
++bound;
}
}
int main() {
int a[10] = { 213,54,213,6213,215,216,8,5,3213,564 };
BubbleSort(a, 10, Greater);
for (int i = 0; i < 10; ++i) {
printf("%d ", a[i]);
}
printf("\n");
BubbleSort(a, 10, Less);
for (int i = 0; i < 10; ++i) {
printf("%d ", a[i]);
}
return 0;
}
2. 表驱动(转移表):
函数指针数组可以用于表驱动法来降低函数的圈复杂度:
#include <stdio.h>
#include <stdlib.h>
int Add(int a, int b) {
return a + b;
}
int Sub(int a, int b) {
return a - b;
}
int Mul(int a, int b) {
return a * b;
}
int Div(int a, int b) {
return a / b;
}
int Menu() {
int input;
printf("=======================\n");
printf("1.Add 2.Sub 3.Mul 4.Div\n");
printf("=======================\n");
printf("输入选择:");
scanf("%d", &input);
return input;
}
int main() {
int x, y;
typedef int(*pFuc)(int, int);
pFuc arr[5] = { 0,Add,Sub,Mul,Div };
int choice = Menu();
printf("输入操作数:");
scanf("%d %d", &x, &y);
printf("结果为%d\n", arr[choice](x, y));
return 0;
}
这个程序比以往switch case语句或者if else语句来的简洁。