函数指针
请问:变量有地址,函数有没有地址呢?
答:函数名也是地址,调用函数时,就是通过函数地址跳转到函数定义的地方执行函数体!既然是地址,那就可以用指针处理,处理指针的函数叫函数指针。
- 函数指针:指向函数的指针,简称函数指针。指向了函数的第一条指令的地址,即存储的是函数的入口地址。
- 我们可以这样理解:将函数理解为一个变量,只是这个变量是一段可执行的指令。
- 使用函数指针可以编写更通用、更灵活的程序。
程序也存储在内存中,与数据一样。
函数的入口地址:函数的第一条指令的地址,称为函数的入口地址。
函数的入口地址:编译器将不带()的函数名解释为函数的入口地址。
调用函数时,实际就是转到函数的入口地址,去执行入口地址的第一条指令。
函数指针和函数原型很类似,只是函数名称前面有一个星号*,并且和函数返回值是指针不同,这里的函数名和指针*要用小括号括起来,表示这个整体是一个指针,这个指针要指向这种函数原型的函数。
函数指针的定义:
数据类型 (*指针名)(参数列表);
例如:
解释:定义一个函数指针add,它指向这样一个函数:该函数的返回值是整型类型,并且有两个整型参数。
定义函数指针时的常见错误:
(1) int *add(int a, int b); // 定义了一个函数add,函数返回值为指向整型数据的指针,并具有两个整型的参数。(其实就只是定义了一个普通函数,在前面加星号*而没有括号只是表明返回值是整型数据的指针,而并不是函数指针!)
(2) int (*add); // 定义了一个指向整型的指针(它就只是一个普通的整型指针,并不指向函数!)
例1:看看如何使用函数指针,以及函数指针有什么作用。
/* 看看如何使用函数指针,以及函数指针有什么作用 */ #include <stdio.h> #include<stdlib.h> int add(int a, int b) { return(a+b); } int sub(int a, int b) { return(a-b); } int add(int a,int b); // 函数声明有分号 int sub(int a,int b); int main() { int (*pf)(int, int); // 定义一个函数指针*pf,返回值是整型,两个参数也是整型 int a,b,c; a=1,b=2; pf=add; // add函数首地址赋给函数指针pf c=(*pf)(a,b); // 通过pf去调用它所指的函数(即add函数),传递的参数为a和b,函数返回值放在c中 printf("%d+%d=%d\n",a,b,c); pf=sub; // sub函数首地址赋给函数指针pf c=pf(a,b); // 当作函数指针使用,不加星号* printf("%d-%d=%d\n",a,b,c); system("pause"); return 0; }
两种调用形式可以互换:
第一种:把它作为指针,调用指针所指对象(指针所指对象是函数)
第二种:把这个指针当作函数指针使用(就可以不加括号和星号*)
函数指针的作用:
可以以一种形态去处理不同的函数,达到了多态的效果。
函数与结构
请问:结构指针(结构和指针结合起来得到的)和结构分别作为函数参数与返回值相比,有哪些优势?
答:当结构变量和结构指针作为参数和返回值时,效率上有很大区别!
如果把结构变量成员、结构变量、结构指针这三类分别作为函数参数与函数返回值,那么就有6种不同的情况:
结构指针传递的都是地址,是通过指针直接对实参的结构空间进行操作,避免了结构空间的分配与成员数据拷贝,效率更高。因此最好用指针进行操作,即用指针变量作为函数参数进行传递,这样由实参向形参传递的只是地址,从而减少了时间和空间的开销,指针作为函数返回值也是一样的道理!
例2:计算一组学生的平均成绩和不及格人数。
分析:假设学生有学号、姓名、性别和成绩。根据要求,我们要有每个学生的成绩,然后根据输入的成绩计算平均成绩并输出。我们给函数取名ave,参数是结构数组首地址和人数,因为函数内部输出了平均成绩,不再需要主调函数输出,所以不需要函数返回值,即函数返回值是void。(这种分析有问题!)
/* 计算一组学生的平均成绩和不及格人数 */ #include <stdio.h> #include<stdlib.h> struct stu{ // 定义stu结构体:学号、姓名、性别、成绩 int num; char *name; char sex; float score; } boy[5]={ // 5个学生信息 {101,"Li ping",'M',45}, {102,"Zhang ping",'M',63.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58} }; void ave(struct stu *ps,int n) // 求平均成绩函数 { int c=0,i; // c是不及格人数 float ave,s=0; for(i=0;i<n;i++,ps++) { s+=ps->score; if(ps->score<60) c+=1; } printf("s=%f\n",s); ave=s/n; printf("average=%f\nNopass student count=%d\n",ave,c); } void main() { void ave(struct stu *ps); ave(boy,5); system("pause"); //return 0; }
这样的分析是有问题的,因为函数返回的结果不是在函数内部把计算结果打在屏幕上就好了,而是应该通过通过函数参数或函数返回值获得计算结果,而不是在函数内部输出结果。调用者想怎么利用函数的结果,那是由调用者的需要决定的,而不是由函数自己决定以什么方式来输出数据。
返回结果不是在函数内部将计算结果打印在屏幕上!
因此我们修改函数的原型,把平均成绩和不及格人数通过返回值给主调函数,但函数的返回值只有一个,因此我们将其中一个返回值作为函数的参数传递给主调函数(传地址调用),因此要返回地址(要加*和&!)。
修改函数原型为:int ave(struct stu *ps, int n, float *average);
第一个参数:数组的首地址
第二个参数:数组元素个数
第三个参数:返回的平均成绩
/* 计算一组学生的平均成绩和不及格人数 */ #include <stdio.h> #include<stdlib.h> struct stu{ // 定义stu结构体:学号、姓名、性别、成绩 int num; char *name; char sex; float score; } boy[5]={ // 5个学生信息 {101,"Li ping",'M',45}, {102,"Zhang ping",'M',63.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58} }; int ace(struct stu *ps,int n,float *average); int ave(struct stu *ps,int n,float *average) // 求平均成绩函数,需要返回不及格人数,因此函数类型为int型 { int c=0,i; float *ave,s=0; for(i=0;i<n;i++,ps++) { s+=ps->score; if(ps->score<60) c+=1; } *average=s/n; return c; // 函数返回值返回不及格人数 } void main() { float average; int m; m=ave(boy,5,&average); // 不及格人数通过函数返回值返回,那么平均分就要用参数传递的方式输出 // 因为要实现双向传递,因此要用传地址调用! printf("avergae is %.2f.No pass count=%d\n",average,m); system("pause"); //return 0; }
心得:
根据需要,如果它有独立的功能,就可以用函数来实现,根据已有的信息和想要得到的结果,我们给出函数的原型,在函数体内部,通过算法将输入转换为输出。函数的输出:通过返回值、或者通过输出参数得到(传地址调用实现双向传递),不要在函数内部使用printf函数输出结果!不能简单地认为函数体内部printf函数就是函数的输出!!!
疑问:两个结果相同,为什么第一种不可以???
补充
(1)指针函数和函数指针(参考:点击此处查看:博主写得十分棒了,手动点赞!)
指针函数:本质是函数,返回值为指针(即返回一个地址值),int *ave(int a, int b);
函数指针:本质是指针,该指针指向某个函数(用于调用函数,调用方式有两种(见下面)),int (*ave)(int a, int b);
函数指针用于调用函数,调用方式下面有两种:
(a) p=*ave();
(b) p=ave();
(2)函数的输出方式
函数的输出方式只有两种:
(a) 通过函数返回值返回
(b) 参数传递(双向传递):传地址调用
注意:不能在函数体内部使用printf函数输出!源程序输出要在主函数内输出!!!
(3)结构体‘.’和'->'的区别(参考:点击此处查看)
(a) 如果定义的是结构体变量,那么用'.',例如:struct stu p; p.num;
(b) 如果定义的是结构体指针,那么用'->',例如:struct stu *p; p->num;
(*p)->num是错误的,因为p是指针,*p就是值类型,那么就应该用(*p).num。即(*p).num=p->num。