函数指针
函数指针:存储函数的地址,可以通过这个地址来调用函数。函数指针的声明需要指定函数返回值的类型、函数名(这里用指针名代替)以及函数的参数列表(包括参数的类型和数量)。但是,在声明函数指针时,我们不会给出函数名,而是使用指针名来代替。
函数指针数组
现在先回忆一下指针数组,它是多个相同变量的地址组成一个指针数组,与函数指针数组有点相像。函数指针数组就是多个相同类型且参数类型也一致的函数组成的函数指针数组。举两个例子吧:
第一个:下面有fun1和fun2两个函数且返回类型和参数相同,这两个函数的函数指针是这样写的void (*pf)(void),把变量(pf)抛开剩下的就是函数指针的类型【void (*) (void)】。接着说说函数指针数组:它就是再函数指针上加一个方括号[]【数组的表示符】即void (*pf[])(void),它的类型【void (*) (void)[]】。函数的地址和&函数名一个意思,所以加不加都无所谓。最后就是打印,函数指针数组说到底是函数组成的所以调用它是还是需要函数调用符()。
第二个:整数的加减乘除,将加减乘除放在pf[5]函数指针数组里然后直接调用函数指针数组就可以。
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,它指向一个数组,而这个数组的每个元素都是一个函数指针且每个函数指针具有相同的返回类型和参数列表。
void fun1()
{
printf("i like father\n");
}
void fun2()
{
printf("i like mother\n");
}
int main()
{
void (*pf[2])(void) = { fun1, fun2 };//函数指针数组
void (*(*ppf)[2])(void) = &pf;//指向函数指针数组的指针
int i = 0;
for (i = 0; i < 2; i++)
{
(*ppf)[i]();//*ppf等价于ppf[0]
}
return 0;
}
下面总结函数指针,函数指针数组和指向函数指针数组的指针三者之间的联系 :
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。
下面是整数的加减乘除的计算,中间运用了回调函数,对于相同操作且重复使用的可以使用回调函数解决问题。
下面讲通过函数 实现库函数qsort的排序(升序为例)
qsort对任何类型的数组都能进行排序,由于qsort是库函数所以直接使用就行,但是作为一个计算机专业的学生需要对它有所了解,下面详细说说:
对于整型数组来说
将数组、数组大小、数组一个元素的字节、一个函数(copy比较相邻元素的大小)这四个参数都写进函数my_sort_int里,这个函数和冒泡排序原理相同,不一样的类型都换成无符号类型,所以可以使用整型或结构体。【my_sort_int(函数名可改变不影响原理)是通用函数】
函数指针将参数强制转换成char*,原因是内存中存放的是十六进制,一个整型是四个字节,对比每个字节的大小。在这个copy函数中返回值分三类(大于、小于、等于0),大于0(说明两个元素为:左>右)为真则进入if语句中,要求为升序则两相邻元素进行交换。
交换则是将内存中的所有数据进行交换,每个字节交换,所以需要知道一个元素有多少个字节(即size)。
对于结构体数组来说
道理与整型数组相同,就不说了看一下运行的流程。 (my_sort_struct就是通用函数)
最后讲一个:void*什么类型的可以使用,但是不能对这个指针进行与运算【例:假设void* p,不能进行p++(元素的大小不知道,加一不知道跳多远)等】