在C语言中,函数存放在内存的代码区,跟基本的数据类型或者数组一样,也有一个内存的起始位置。函数的类型由其参数以及返回类型共同决定,跟函数名无关。

一个简单的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int (*pf)(int, int);
    pf = add;
    printf("   pf(1, 2) = %d\n", pf(1, 2));
    printf("(*pf)(1, 2) = %d\n", (*pf)(1, 2));
}
// 编译执行可以正常输出:
// cc demo1.c -o demo1
// demo1
//    pf(1, 2) = 3
// (*pf)(1, 2) = 3

函数指针定义

直接定义

返回数据类型 (*函数指针名称) (入参类型列表逗号分隔)

例子 int (*pf)(int, int);

使用typedef定义

typedef 返回数据类型 (*函数指针类型名称) (入参类型列表逗号分隔)

例子

1
2
type int (*PF)(int, int);
PF pf; 

此时PF是指针函数类型,不是真正的函数指针变量,需要使用该类型定义出函数指针变量使用。

函数指针的使用

作为变量

如同开头简单的例子所示,函数指针变量作为普通变量几乎没什么意义,还不如直接调用函数。放到结构体中,是不是有点像C++或者Java面向对象的味道了?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

struct Student {
    unsigned int id;
    char *name;
    void (*print)(struct Student *stu);
};

void PrintInfo(struct Student *stu) {
    printf("%d -- %s\n", stu->id, stu->name);
}

int main() {
    struct Student stu = {1, "raveh", PrintInfo};
    stu.print(&stu);
}
// 编译执行可以正常输出:
// cc demo2.c -o demo2
// demo2
// 1 -- raveh

作为函数形参

一个例子,可以让数据给不同的函数去处理:

 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
#include <stdio.h>
#include <stdlib.h>

int max(int a, int b) {
    return a > b ? a : b;
}

int min(int a, int b) {
    return a > b ? b : a;
}

void proc(int a, int b, int(*pf)(int, int)) {
    printf("a=%d, b=%d, pf(%d,%d)=%d\n", a, b, a, b, pf(a, b));
}

int main() {
    int a = rand(), b = rand();
    proc(a, b, max);
    proc(a, b, min);
}
// 编译执行可以正常输出:
// cc demo3.c -o demo3
// demo3
// a=1804289383, b=846930886, pf(1804289383,846930886)=1804289383
// a=1804289383, b=846930886, pf(1804289383,846930886)=846930886

这个程序可以使用typedef定义一个函数指针,使proc函数的参数列表看起来更清晰一点。

 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
#include <stdio.h>
#include <stdlib.h>

typedef int(*PF)(int, int);

int max(int a, int b) {
    return a > b ? a : b;
}

int min(int a, int b) {
    return a > b ? b : a;
}

void proc(int a, int b, PF pf) {
    printf("a=%d, b=%d, pf(%d,%d)=%d\n", a, b, a, b, pf(a, b));
}

int main() {
    int a = rand(), b = rand();
    proc(a, b, max);
    proc(a, b, min);
}
// 编译执行可以正常输出:
// cc demo4.c -o demo4
// demo4
// a=1804289383, b=846930886, pf(1804289383,846930886)=1804289383
// a=1804289383, b=846930886, pf(1804289383,846930886)=846930886

作为函数返回值

 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
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>

int max(int a, int b) {
    return a > b ? a : b;
}

int min(int a, int b) {
    return a > b ? b : a;
}

/**
 * 可以按照由内向外的顺序看这个函数的声明
 * getFun(char oper)有形参列表,则getFun是一个函数
 * getFun前面有*,所以getFun返回一个指针
 * 返回的指针本身也包含形参列表(int,int)
 * 因此返回的指针指向一个返回值为int的函数
 */
int (*getFun(char oper))(int, int) {
    switch(oper) {
        case '>':
            return max;
            break;
        case '<':
            return min;
            break;
    }
    return NULL;
}

int proc(int a, int b, char oper) {
    int(*pf)(int,int) = getFun(oper);
    if(pf) {
        return pf(a, b);
    }
    return -1;
}

int main() {
    int a = rand(), b = rand();
    printf("proc(%d, %d, %c) = %d\n", a, b, '>', proc(a, b, '>'));
    printf("proc(%d, %d, %c) = %d\n", a, b, '<', proc(a, b, '<'));
}
// 编译执行可以正常输出:
// cc demo5.c -o demo5
// demo5
// proc(1804289383, 846930886, >) = 1804289383
// proc(1804289383, 846930886, <) = 846930886