目录
一:函数指针
1.函数指针的创建
- 函数指针,定语是函数,本质上是一个指针,合起来就是指向一个函数的指针
- 创建一个函数指针时我们只要清楚下面这几个点,就可以轻松创建一个函数指针
- 函数指针的名字
- 函数指针指向的函数的参数类型
- 函数指针指向的函数的返回值
- 下面一个小例子帮你更好的理解如何创建一个函数指针
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int x = 1;
int y = 2;
int sum = 0;
int (*p)(int,int) = &Add;
sum=(*p)(x, y);
printf("%d", sum);
return 0;
}
-
我们首先看到第三行,这里我创建了一个函数叫做Add,两个参数都为int类型,返回值也为int类型
-
再看到代码的第十二行,这一行我们创建了一个函数指针
-
其中序号1位置的p是这个函数指针的名字
-
p前面的*代表p是一个指针,后面又跟着一个(),这个括号代表指针p是指向一个函数
-
(*p)后的()里面是 (int,int),对应上方Add函数的两个参数类型
-
(*p)前方的int对应Add函数的返回类型int
-
强调:(*p)必须要在括号里面,写成 int *p (int,int)=&Add是错误的写法
-
因为不把p放到括号里面的话,p会先于和后面的()结合,编译器会把p当成一个函数,而不是一个指针
-
而把p用括号括起来时,表明了p先是一个指针,然后再和后面的括号结合表明这个指针是指向一个函数
-
同理,使用(p)调用Add函数时也必须在p两旁加上(),否则你会收获一个编译器的报错
-
p存放了Add函数的地址,对p进行解引用(*p)就是找到了Add,(*p)(x,y)等价于Add(x,y)
2.&函数名与函数名
- 我们知道数组名代表数组首元素的地址,那函数名是不是代表函数首元素的地址呢?
- 可是函数没有首元素这一说,所以函数名其实就是代表函数的地址
- 也就是说&Add和Add其实是等价的
- - 可以看到我把&Add换成Add,结果仍然没有变
- 我们再想想,Add函数的地址能被函数指针p接收,而Add就代表其地址,那p是不是可以和Add等价呢
- 可以看到,我没有使用(*p)(x,y)而是使用p(x,y),结果仍然正确,编译器也没有报错
- 说明了p其实是和Add等价的,那么前面的*其实也没有什么作用了
- 所以我们无论往p前面加多少个*,都不影响我们的结果
二:函数指针数组
1.函数指针数组的创建
- 讲完了函数指针,我们再来看看函数指针数组
- 函数指针数组,看起来好像很难理解,我们可以这么看(函数指针)数组
- 说明它本质上是一个数组,但是数组中存放的是函数指针
- 我们只要把上面创建函数指针的步骤稍稍变一变,就能创建一个函数指针数组
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int x = 1;
int y = 2;
int sum = 0;
int cha = 0;
int (*p[2])(int, int) = { Add,Sub};
sum = p[0](x, y);
cha = p[1](x, y);
printf("%d\n", sum);
printf("%d", cha);
return 0;
}
- 看到代码的第17行,原本的 int (*p) (int,int)变成了 int (*p[2]) (int,int)
- 就是在p后面加了个[2],我们就创建了一个函数指针数组
- 我们可以这么看这一行代码,[] 和的优先级是[]高于,所以p先和[2]结合,表明p是一个数组
- 然后我们把p[2]摘掉,剩下int (*)(int,int),这代表p这个数组中存放的数据类型
- 通过上文我们可知,这个类型是一个函数指针,(一个参数为(int,int),返回值为int的函数)的指针
- 所以p就是一个能够存放两个函数指针的数组
- 要想调用里面的函数和我们之前使用数组并无区别,通过p[0]就能找到Add函数,再加上参数,就能完成Add函数的调用
- 而换成p[1]就能调用Sub函数
2.指向函数指针数组的数组指针
- 难度再升级一下,我们知道了如何创建一个函数指针数组,那我们想用一个指针存放这个数组,这个指针如何创建呢?
- 首先我们要明白这个指针指向的是一个函数指针数组,简单说就是指向一个数组,所以这个指针是一个数组指针
- 我们再把上面创建函数指针数组的代码变一变, 变成 int (*(*p)[2]) (int,int)
- (*p)说明p是一个指针,后面跟的[2]代表这个指针指向的是一个数组
- 然后挖去(p)[2],剩下int()(int,int)是这个(数组指针)指向的(数组的元素)的类型为函数指针
- 一起看就代表了指向一个函数指针数组的数组指针
三:void(*signal(int,void(*)(int)))(int)的意义
- 有了前面的知识的铺垫,最后再来看看《C陷阱和缺陷》这本书提到的一个代码
- 这看起来简直复杂极了,我们一层一层看,先看signal(int,void(*)(int))
- 这样是不是就比较清晰了,signal是函数名,它有两个参数,一个为int,另一个是函数指针(void(*)(int))
- 这个函数指针指向一个参数为int,返回值为void的函数
- 然后去掉signal(int,void(*)(int)),剩下void(*)(int)是signal这个函数的返回值的类型
- 说明signal函数的返回值的类型也是一个函数指针
- 这个函数指针也指向一个参数为int,返回值为void的函数
- 或许这样写你会比较清晰void(*)(int)signal(int,void(*)(int)),但是C语言不支持这样的语法
- 我们可以用typedef帮助我们理解
- 我们讲void(*)(int)这个函数指针类型重定义为abc
- 然后原本复杂的代码就可以写为abc signal(int, abc);
- 综上所述,void(signal(int,void()(int)))(int)这句话其实是一个函数signal的声明