函数指针

函数指针就是指向函数的指针,其值就是函数体的首地址。在底层,函数名就代表函数的首地址,所以把函数名直接指派给一个同类型的函数指针而不需要&运算符,可以直接用函数名注册回调函数

调用的方式

1
2
3
4
5
size_t(*pf)(const char*) = &strlen;
// &操作符为可选的,函数名被使用时编译器会把它转换为函数指针,&操作符只是显式地说明了编译器将隐式执行的任务
int len = strlen("hehe");
len = (*pf)("hehe");
len = pf("hehe");

第一句简单地使用名字strlen直接调用函数,实际上执行过程是函数名strlen首先被转换成一个函数指针,该指针指向内存中函数的位置,然后函数调用操作符调用该函数,执行开始于这个地址的代码
第二句对pf执行间接访问操作,它把函数指针转换为一个函数名。这个转换并不真正需要,编译器在执行函数调用操作符之前又会把它转换回去
第三句和直接通过函数指针访问,这三句在效果上都是一样的

两段有意思的代码

1
2
3
4
5
// 代码一
(*(void(*)())0)();

// 代码二
void(*signal(int, void(*)(int)))(int);

首先代码一(*(void(*)())0)()(void(*)())0单独拿出来这是将int类型的0强转成void(*)()函数指针类型,然后在解引用,最后对函数指针解引用后调用
用typedef简化:
typedef void(*pFuc)();
(*(pFuc)0)(); // 代码一等价于这个形式

再来看看代码二void(*signal(int, void(*)(int)))(int);signal(int, void(*)(int))单独拿出来,发现这是一个函数名为signal,形参为一个int类型和一个void(*)(int)的函数指针类型,外面的一层void(* )(int)是修饰其返回值类型的
用typedef简化:
typedef int(*pFuc)(int);
pfuc signal(int, pFuc);

应用场景

回调函数

函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数
这里给出一段改进版的冒泡排序,可以做到一份代码同时实现升序和降序两种功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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;
}

int Less(int a, int b) {
return a < b;
}

void BubbleSort(int* arr, int size, Comp p) {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size - 1 - i; ++j) {
if (p(arr[j + 1], arr[j])) {
Swap(&arr[j], &arr[j + 1]);
}
}
}
}

int main() {
int a[5] = { 1999, 723, 1007, 2019, 1212 };
BubbleSort(a, 5, Greater); // 降序
for (int i = 0; i < 5; ++i) {
printf("%d ", a[i]);
}
putchar('\n');
BubbleSort(a, 5, Less); // 升序
for (int i = 0; i < 5; ++i) {
printf("%d ", a[i]);
}
putchar('\n');
return 0;
}

转移表

转移表就是一个函数指针数组,可以降低函数的圈复杂度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}