在C语言里,与数组的数组名是数组的首地址一样,一个函数的函数名,也是该函数的首地址。该怎么使用呢?
例子:定义一个函数如下:
int maxValue(inta,int b) {
return a>b?a:b;
}
那么这个函数的地址就是maxValue。
此时引出“函数指针”的定义方式:
int( *p)(int , int)=NULL;
这个该怎么解释?
“定义一个p变量,这个变量是一个指针,这个指针指向一个函数,函数的参数有两个,都是int类型的,这个函数的返回值是int类型的。”
当任意多个函数的参数个数、参数类型、返回值类型都一样时,这个函数的类型就是一样的。因此在定义函数指针的时候,可以定义一个指向NULL的函数指针,然后分别将各个函数名赋值给这个指针。即:
int( *p)(int,int)=NULL;
p = maxValue;
定义好后,就可以直接使用这个p了,与普通调用函数一样,只需要将其写下来,把参数给上,接收返回值就行了。
int num = p(3,5);
一些练习,定义几个函数,分别定义这些函数的函数指针。
#import //返回最大值
//函数的地址:函数名 //函数的类型:返回值类型 +参数个数和参数类型。 int maxValue(int num1,int num2 ){ return num1 > num2 ? num1 : num2; }
//a函数和maxValue的函数类型相同,可以使用相同的函数指针。 int a(int n1,int n2){ return 1; } //把一下函数的函数指针写出来 void b(float n1,float n2){
}
void c(){
}
float d(int n1){ return 1.2; } int main(int argc,constchar * argv[]) { //2015-04-09 09:47:33北京
// //定义一个函数指针的方式 // int(*p1)(int,int) = maxValue; // //a函数和maxValue的函数类型相同,可以使用相同的函数指针。 //// p1 = a; // //b函数的指针 // void (*p2)(float , float) = b; // //c函数的指针 // void (*p3)() = c; // //d函数的指针 // float (*p4) (int) = d; // // printf("%d\n",maxValue(10, 20));//20 // // int n1 = p1(10,20);//指针名调用 // printf("%d\n",n1);//20 return 0; } |
一个练习:
void printHello();
定义⼀个可以指向上述函数的函数指针,并通过函数指针实现调⽤该函数。
#import //练习1 void printHello(){ printf("hello world!\n"); }
int main(int argc,constchar * argv[]) { //l练习 // //函数指针p1 // void(*p1)() = printHello; // //指针调用 // p1();//hello world! return 0; } |
一个练习:
定义两个函数,⼀个求最⼤值,⼀个求和,输⼊max或sum分别求3,5的最⼤值或和(提⽰,定义⼀个函数指针,根据输⼊内容指向不同函数,最后⼀次调⽤完成)。
#import //练习2 int sum(int num1,int num2){ return num1 + num2; } //获得一个值 //函数指针做参数,可以屏蔽核心代码 int getValue(int num1,int num2,int (*pv)(int,int)){
//核心代码 //通过指针再去调用函数,称为回调。 return pv(num1,num2); } int main(int argc,constchar * argv[]) { // char str[20] = {0}; // printf("请输入maxValue或者sum完成不同功能:"); // scanf("%s",str); // // int(*pf)(int,int) = NULL; // // if (strcmp("maxValue", str) == 0) { // pf = maxValue; // }else if(strcmp("sum", str) == 0){ // pf = sum; // }else{ // printf("输入有误!"); // } // //调用函数 // if (pf != NULL) { // int n = pf(3,5); // printf("%d\n",n ); // }else{ // return 0; // } //函数回调:函数只在乎你作为函数参数 // int (*p1) (int ,int ) = maxValue; // int (*p2) (int ,int ) = sum; // // getValue(3, 5, p1);//5 // getValue(5, 6, p2);//11 return 0; } |
在一个函数中,如果将一个函数指针作为它的一个参数,那么这个函数指针可以起到一个保护核心代码的作用。
如上个例子,在getValue()中,定义了int num1,num2,int(*pv)(int int)三个参数,其中第三个参数为函数指针。那么此时调用getValue()这个函数的时候,接收到三个参数3, 5, p1。p1 = maxValue。此时,进到函数内,执行pv(num1,num2);pv为函数指针,此时,我们的p1将自己所指向的函数地址,给pv。那么此时的pv将num1和num2两个参数给到它所指向的那个函数int sum(int num1,int num2)去执行,得到的结果再返回给用户。这样就可以起到保护核心代码的作用,为什么这么说?因为我可以不用改那个函数里边的代码,只是在这个函数的参数预留你想要的接口(一般是函数指针)。此时,只需要给这个参数一个函数地址,即可找到一个函数实现你想要的结果,不需要再改变原来函数的代码。
一些例子:
写⼀函数查找成绩90分以上的学员,使⽤回调函数在姓名后加”⾼富帅”。
#import //练习
typedefstruct Student{ char name[20]; float score; }Student; void search(Student stuArr[],int count,void (*ps)(Student *)){ for (int i = 0; i < count ; i++) { if ((stuArr+i)->score >=90) { //添加"高富帅" ps(&stuArr[i]); } } }
void setName(Student *stu){ strcat(stu->name,"高富帅"); } int main(int argc,constchar * argv[]){ //练习 // Student s1 = {"王大锤",90}; // Student s2 = {"王大",89}; // Student s3 = {"王大",99}; // Student s4 = {"王大",91}; // // Student stu_arr[4] = {s1,s2,s3,s4}; // void (*p1)(Student *) = setName; // // search(stu_arr, 4,p1); // // for (int i = 0; i < 4; i ++) { // printf("%s\n",stu_arr[i].name); // } return 0; } |
上面这些,将函数指针作为函数参数的做法,叫做函数回调。
说白了,就是在函数里调其他函数,只是不是明调罢了,只是将地址给它,让它根据地址去指定的地方执行。
动态排序的意思是根据不明确的需求(即会根据不同的条件,进行不同的排序),来对数据进行排序。这个是函数回调(函数指针做函数参数)的一个经典例子。这类问题的关键是,根据不同的条件,这些“不同的条件”就是关键。
#import //从小到大排序 void sort (int arr[],int count,BOOL(*pf)(int,int)){ for (int i = 0; i < count -1 ; i ++) { for (int j = 0; j < count - (i + 1); j ++) { if( pf(arr[j] , arr[j+1]) ){//判断是关键
arr[j] = arr[j] ^ arr[j + 1]; arr[j+1] = arr[j] ^ arr[j+1]; arr[j] = arr[j] ^ arr[j + 1]; }
} }
}
BOOL cmpFunc(int num1,int num2){ return num1 > num2; } int main(int argc,constchar * argv[]) { //动态排序 // int arr[5] = {4,3,1,2,5}; // // BOOL (*p1)(int,int) = cmpFunc; // // // sort(arr,5,p1); // // printf("小到大排序:\n"); // for (int i = 0 ; i < 5 ; i ++) { // printf("%d\t",arr[i]); // } return 0; }
|
冒泡排序,在if这个条件中,放入pf(arr[j] , arr[j+1])这个函数指针,可根据不同的返回值,进行不同的排序。
一个例子:
#import //学生结构体 typedefstruct Student{ char name[20]; float score ; int age; }Student;
void sortStudent(Student stu[],int count,BOOL (*p)(Student *,Student *)){ for (int i = 0; i < count -1 ; i ++) { for (int j = 0; j < count - i -1; j++) { if(p(&stu[j],&stu[j+1])){ Student temp = {0}; temp = stu[j]; stu[j] = stu[j+1]; stu[j+1] = temp; } } } }
//按成绩排 BOOL byScore(Student *s1 ,Student *s2){ return s1->score > s2->score; } //按年龄排 BOOL byAge(Student *s1 ,Student *s2){ return s1->age > s2->age; } //按姓名排序 BOOL byName(Student *s1 ,Student *s2){ if (strcmp(s1->name , s2->name)>0) { returnYES; }else{ returnNO; } } int main(int argc,constchar * argv[]) {
// 2015-04-09 15:13:52北京
Student stu[5] = {{"a",99,16},{"b",70,19},{"c",88,20},{"d",60,15},{"e",80,0}}; // BOOL (*sp)(Student *,Student *) = byScore; BOOL (*ap)(Student *,Student *) =byAge; BOOL (*np)(Student *,Student *) =byName;
// sortStudent(stu, 5,sp); sortStudent(stu, 5, ap); sortStudent(stu, 5, np); // sortStudentByName(stu, 5, np); for (int i = 0; i < 5; i ++) { printf("%s:%.2f:%d\n",stu[i].name,stu[i].score,stu[i].age);
}
return 0; } |
这里函数指针作为返回值,用到了typedef的第二种用法,专门给一个函数指针类型取一个名字。这样就可以直接用这个名字来作为另一个函数的返回值。
其实,* 号可以写在typedefint *pFun(int,int);也可以写在定义函数的时候pFun *fun2(char str[])。看情况,没有具体要求。
//typedef的第二种用法,专门给函数指针类型取一个心名字 typedefint pFun(int,int);
pFun *fun2(char str[]){ returnNULL; } |
一个例子:
#import //typedef的第二种用法,专门给函数指针类型取一个心名字 typedefint pFun(int,int);
pFun *fun2(char str[]){ returnNULL; } // + - * / //定义了加减乘除四个函数,每个函数首地址都为函数名。 //四个函数类型相同。 int sum(int num1 ,int num2){ return num1 + num2; } int sub(int num1 ,int num2){ return num1 - num2; } int mul(int num1 ,int num2){ return num1 * num2; } int di(int num1,int num2){ if (num2!=0) { return num1 / num2; }else{ return 0; } } //给函数类型起了一个新名字。Cal。 typedefint Cal(int ,int ); //定义一个结构体,用来一一对应名字和函数指针匹配(名字与函数建立对应关系) typedefstruct FunNamePair{ char name[20]; Cal *pFun; }FunNamePair;
Cal *searchFun(char str[]){
FunNamePair f[4] = {{"sum",sum},{"sub",sub },{"mul",mul },{"di",di }}; for (int i = 0; i < 4; i ++) { if (strcmp(str, f[i].name) == 0) { return f[i].pFun; } } returnNULL; } int main(int argc,constchar * argv[]) { // 2015-04-09 16:00:49北京 Cal *p = searchFun("mul"); if(p !=NULL){ printf("%d\n",p(3,5)); }else{ printf("输入有误。"); } return 0; } |
这样,C语言告一段落。