函数指针与回调函数新感悟
2021年5月22日12:57:47
关于函数指针的理解可以参考之前写的博文。
对于一个指针而言,我们需要考察它的几点性质:
- 1、这个指针是什么类型的?
- 2、这个指针的大小是多少?
- 3、这个指针是什么时候指向什么地址的?
其实,我们对于一个指针一般只关注两点,1和3.
对于1,肯定是在声明的时候就知道这个指针应该指向什么类型。
对于2,肯定是在赋值的时候知道它指向对应的类型的地址的。
对于3,指针的大小和系统有关,一般的系统,指针大小为4字节,也就是和int类型的大小一样。
明确以上1,2点之后,我们对照着int *来看就会很明白了。
int a = 0;
int * p;
p = &a;
那么接下来我们聊聊函数指针。
现在再回顾一下,我用自己的语言来描述一下什么是函数指针。
函数指针 本质是一个指针。
结合上面关于指针的2点需要关注的点,我们对照一下函数指针该怎么去解释或者说理解。
像声明int类型的指针一样,我们应该声明一个函数类型的指针。
这个时候的疑问是,系统里没有我们需要的函数类型啊。
那怎么办?
别忘了一件事:
C语言为我们提供了声明自定义类型的工具:那就是typedef关键字。
我们一般使用这个关键字做结构体的声明,在声明结构体的时候还要结合另外一个关键字struct。
typedef struct person_s { //这里后缀s是认为添加的,我们可以理解成使用person_s这个结构体时候,必须和struct一起使用,因为现在的结构体名字是struct person_s 而不是person_s
int age;
char sex;
double height;
double weight;
};
从上面可以看到typedef的一种用法就是用来声明结构体。
还有另外一种就是为类型取别名。
typedef struct person_s person_t;
通过上面这个语句,我们就成功的为struct person_s 取了一个别名,也就是一个外号:叫person_t。 这个时候我们在需要使用struct person_s 的地方可以使用person_t进行替换了,是不是方便很多?
ok。
到这里我们认识了typedef的取别名的用法。
回到正题。声明函数类型的指针,肯定需要一个函数类型了。
那么我们就用typedef来声明一个函数为了就行了。
typedef void * (*FP)(int *, int);
上面这句话就是为函数void * (*)(int *, int);
取了一个别名叫FP。
那么我们就可以使用fp去声明一个指针了。
FP *fp1; //这样fp1就必须指向一个形如void * (*)(int *, int)的函数。
int a[] = {1,2,3,4,5,6,7,8};
int n = 3;
void * sumArray(a, n);//此函数在其他地方声明和实现的,计算一个数组前n的和并返回该值的地址(这个例子不好,主要是返回内容是一个地址了)
//上面这个函数就是FP类型的。我们可以使用fp1来替换sumArray函数进行调用
fp1 = sumArray;
fp1(a,n);//这样就可以和直接调用sumArray一样的效果。
好,从上面的例子,我们隐约能够觉得,函数指针是可以被赋值的,也是可以当做被赋值过来的函数进行调用的。
慢慢品味这里的逻辑。
有了这个意识之后,我们可以更大胆一点。
如果在一个结构体里声明了一个函数指针,那么会发生什么?
看例子:
typedef void (*EAT)(char *, int);//吃的方法,传入水果的名字和数量,表示吃了几个XX水果
void (*_eat)(char * name, int n){
//吃了n个name水果
}
typedef struct person_s{
int age;
EAT *eat;//
}person_t;
person_t *xiaoming = (person_t)malloc(sizeof(person_t));//给xiaoming申请一块内存
//开始给xiaoming赋值
xiaoming->age =18;
xiaoming->eat = _eat;//注意这里为xiaoming的eat方法实际赋值了,赋值为_eat方法,看清这里,是直接把函数名赋值给对应的指针
//程序某处需要调用xiaoming的eat方法
char *name = "Orange";
int n = 2;
xiaoming->eat(name,n); //开始执行_eat函数。
这个函数的赋值,和在响应需要的时候调用指定函数显示不同的功能的过程,就叫做函数的回调。这个函数就是回调函数。