函数指针其实就是函数地址,
准确的说应该是函数代码所在空间的第一个字节的地址,即整个函数的入口

C语言中,函数名直接就代表函数指针

指针函数、函数指针
这两个名称很容易混淆,但是本小节介绍完后,保证大家一定能够分清这两个概念


一、指针函数

如何理解“指针函数”这个词?

前面的“指针”两个字为形容词,后面的“函数”两个字为名词,
所以这句话的全称为“返回值为指针类型的函数”,所以“指针函数”的本质是函数

int *fun(void)
{
    int *p = NULL;
    p = malloc(sizeof(4));
    return p;
}

二、函数指针

同样的,前面“函数”为形容词,后面的“指针”为名词,这句话的全称应该叫“函数的指针”,
也就是函数的地址,所以函数指针的本质是“指针”,自然存放“函数指针”的变量就是“函数指针变量”

1. 函数指针类型

函数指针的类型比较怪异,跟一般的指针形式不一样,
由于是函数指针类型与函数有关,所以长的与函数的样子很像

比如:

int fun(void)
{
    ...
}

fun为函数指针,函数指针的类型为int (*)(void)


int *fun(int a, int b)
{
    ...
}

fun为函数指针,指针的类型为int *(*)(int, int),第一个*是返回值类型int *的组成部分。


2. 定义函数指针变量

1)定义普通的函数指针变量

定义时只需要在*后面加上变量名即可。

int *p; 
float *p;
char *p;
...
2)定义函数指针变量

也是类似的,在*后面加上变量名即可,比如:

int *fun(int a, int b)
{
    ...
}

定义一级函数指针变量:

int *(*funp)(int, int) = &fun; //&fun的类型为int *(*)(int, int)

在前面就说过,&可以省略,&fun等价于fun

定义二级函数指针变量:

int *(**funp)(int, int) = &funp; //&funp的类型为int *(**)(int, int)
定义一级函数指针数组:`int *(*funp[4])(int, int);`
定义二级函数指针数组:`int *(**funp[4])(int, int);`
...

为了表述的方便,以后我们会将“函数指针”和“函数指针变量”都统称为函数指针,
具体指哪一个,就看说话的语境

3)函数指针类型中的()一定不能省略

否则就变成了函数的定义形式,比如:

int (*funp)(void);

()省略后:

int *funp(void);

对于int *funp(void)来说,()的优先级高于*的优先级,funp会先和(void)结合,
所以编译器在编译时就会把funp理解是函数,但是又没有函数体{...},所以编译器最终会报错

但是加了括号后就变为了int (*funp)(void)*就会优先和funp结合,
那么编译器就会把funp理解为指针变量,所以括号一定不能省略,省略后含义完全不一样


3. 进行强制转换时,应该怎么写

1)使用普通指针类型进行强制转换时

float a = 123.42;
int *p = (int *)&a;

2)使用函数指针类型进行强制转换时

int (*funp)(void) = (int (*)(void))addr;  // 括号() 里面就是函数指针类型
int (**funp)(void) = (int (**)(void))addr;
...