1:回调函数的简单说明
回调函数概念,和应用场景。其实是比较复杂的,但是我们尝试从易到难。
1:我们先了解回调函数,先看代码
-
#include<stdio.h>
int test1(int x,int y)
{
printf("x+y=%d\n",(x+y));
return 0;
}
void test2( int (*p)(int ,int )) //这里使用int(*p)();也是可行的,推荐是用这种,不带参数声明的,
{
int y=1;
int z=2;
(*p)(y,z);
}
int main()
{
test2(test1);
return 0;
}
至少存在3个函数(回调函数+回调者函数+主函数(不一定要是主函数也可以是任意合法函数))
函数2中必须存在一个形参,这个形参必须是函数指针类型void (*p)(),注意空函数指针也能作为形参。而且空指针称为垃圾桶指针(注意不是垃圾指针),什么类型的指针都能赋值给它
我们在函数1(假设为main主函数)中调用test2,此时该把什么参数传递给test2void (*p)()
的形参吗?是不是应该给个函数名(本质是该函数的入口地址),于是我们把tset1作为实参,传递出去。
此时,代码跑到test2函数内部。此时我们使用(*p)(y,z),来调用函数test1。这就实现了,在没有调用test1的情况下,实现了test1的功能。
现在让我们把 情况复杂化,假如test2是封装在头文件,或者库中的函数。这个库或头文件中的函数,必须要调用客户自定义的一个函数test1或者要接收test放回的一个值。这时回调函数的意义和作用就显现出来了。
这时有同学问了,什么我大名鼎鼎,无所不能的库函数还要去调用,用户定义的函数,不可能绝对不可能!!
莫急,我们直接来看,c中的qsort()函数。这是关于对数组,字符串自动进行排序的一个库函数。
2:通过详细的示例解释回调函数
我们先来看看这个函数的原型
viod qsort()表明其函数没有返回值。
第一个参数:void *base 是需要排序的字符或数组的首地址。这里必须声明的一点是,void声明一个指针变量,是C中比较特殊的一种用法。
printf("我们定义一个viod类型的指针*pvoid:");
void *pvoid;
printf("打印此空指针的地址%p\n",&pvoid);
printf("打印此空指针的大小%d\n",sizeof(pvoid));
printf("看看此空指针+1后地址移动了几位%d",(pvoid+1)-pvoid);
查看一下输出结果:
可以看到void类型指针,系统是一样的为其开辟了内存空间,大小同其他指针变量一样都是8个字节,可以进行算术运算,和自增自减操作。
空指针还能作为函数形参(也就是被调用函数中的参数),此种情况下,任何指针变量和指针常量都能传入到形参中去。但是在实际使用时,必须强制转换。才能正确使用。
注意,void修饰指针变量是合法操作,但是千万不能用void来修饰普通变量。我们在函数声明或定义时,viod fun_1(void),表明,该函数是没有参数的。
看第二个参数:size_t mun,是表明该需要排列数组或字符串的个数。
第三个参数:size_t width ,是表明需要排列数组或字符,每一个元素的Byte数,如int arr1[],这个参数就是4,char arr2[],z这个参数就是1。
最后一个参数:一个函数指针,是学习的重点 int(cdcel_*compare)(const void *element1,const void *element));
这是一个函数指针,作为库函数的形参。那么这个参数就可以被传递各种各样的函数名。
于是接下来,我就自己写了个排序函数,并且利用了回调函数的性质。
/*第一个函数是交换函数*/
void Swap(char*e1,char*e2,int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
int cmp_my(const void* e1, const void* e2)
{
return *((int*)e1) - (*(int*)e2);
}
void SelfDefine_sort(void *base,int sz,int width,int (*p)(),void(*p2)())
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - (1 +i); j++)
{
if (p((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
p2((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
void print_char(char *arr,int sz)
{
for (int i = 0; i < sz; i++)
printf("%c ", arr[i]);
}
void print_int(int *arr,int sz)
{
for (int i = 0; i < sz; i++)
printf("%d ", arr[i]);
}
#include<stdio.h>
#include<string.h>
int main()
{
/*先比较int类型的数组*/
int arr[] = { 90,889,767,56,51,1114,323,21,100 };
int sz = sizeof(arr) / sizeof(arr[0]);
print_int(arr, sz);
printf("\n");
SelfDefine_sort(arr, sz, sizeof(arr[0]), cmp_my,Swap);
print_int(arr, sz);
/*再比较char类型的字符数组*/
char Arr_char[] = "WOAIwangjiahang";
int sz1 = sizeof(Arr_char) / sizeof(Arr_char[0]);
print_char(Arr_char, sz1);
printf("\n");
SelfDefine_sort(Arr_char, sz1, sizeof(Arr_char[0]), cmp_my,Swap);
print_char(Arr_char, sz1);
return 0;
}
有了上面的基础,要写出返回类型为函数指针的函数应该不难了,下面这个例子就是返回类型为函数指针的函数:
void (* func5(int, int, float ))(int, int)
{
...
}
在这里, func5 以 (int, int, float) 为参数,其返回类型为 void (*)(int, int) 。
在开始讲解回调函数前,最后介绍一下函数指针数组。既然函数指针也是指针,那我们就可以用数组来存放函数指针。下面我们看一个函数指针数组的例子:
-
/* 方法1 */
-
void (*func_array_1[5])(int, int, float);
-
/* 方法2 */
-
typedef void (*p_func_array)(int, int, float);
-
p_func_array func_array_2[5];
上面两种方法都可以用来定义函数指针数组,它们定义了一个元素个数为5,类型是 void (*)(int, int, float)
的函数指针数组。