指针作为函数参数 指针可以作为函数的参数。
在函数章节中,我们把数字作为参数传入函数中,实际上就 是利用了传递指针(即传递数组的首地址)的方法。通过首地址,我们可以访问数组中的任 何一个元素。 对于指向其他类型变量的指针,我们可以用同样的方式处理。
例如,我们编写如下一个函数,用于将两个整型变量的值交换。
1. void swap(int *x,int *y) 2. { 3. int t=*x;*x=*y;*y=t; 4. }
这时,我们在其他函数中可以使用这个函数:
1. int a=5,b=3; 2. swap(&a,&b); 3. printf(“a=%d,b=%d”,a,b); //输出:a=3,b=5
在这个过程中,我们先将 a 和 b 的地址传给函数,然后在函数中通过地址得到变量 a 和 b 的值,并且对它们进行修改。当退出函数时,a 和 b 的值就已经交换了。
这里有一点值得我们注意。看如下这个过程:
1. void swap(int x,int y) 2. { 3. int t=x;x=y; y=t; 4. }
我们调用了 swap(a,b);然而这个函数没有起作用,没有将 a 和 b 的值互换。 为什么呢?因为这里在传入变量 a 和 b 的时候,是将 a 的值赋值给函数中的形参 x,将 b 赋值给形参 y。这里接下来的操作就完全与 a 和 b 无关了,函数将变量 x 和 y 的值互换, 然后退出函数。这里没有像上面例子那样传入指针,所以无法对传进来的变量进行修改。
将指针传入函数与将变量传入函数的区别在于:前者是通过指针来使用或修改传入的变量;而后者是将传入的变量的值赋给新的变量,函数对新的变量进行操作。
同理,对 scanf()函数而言,读取变量的时候我们要在变量之前加&运算符,即将指针传入函数。这是由于 scanf()函数通过指针将读取的值返回给引用的变量,没有&,就无法 进行正常的读取操作。
【例 9】编写一个函数,将三个整型变量排序,并将三者中的最小值赋给第一个变量,次小值赋给第二个变量,最大值赋给第三个变量。
1. #include<cstdio> 2. using namespace std; 3. void swap(int *x,int *y) 4. { 5. int t=*x;*x=*y;*y=t; 6. } 7. void sort(int *x,int *y,int *z) 8. { 9. if(*x>*y) swap(x,y); 10. if(*x>*z) swap(x,z); 11. if(*y>*z) swap(y,z); 12. } 13. int main() 14. { 15. int a,b,c; 16. scanf("%d%d%d",&a,&b,&c); 17. sort(&a,&b,&c); 18. printf("%d %d %d",a,b,c); 19. return 0; 20. }
输入:2 3 1
输出:1 2 3
函数返回指针
一个函数可以返回整数值、字符值、实型值等,也可以返回指针联系的数据(即地址)。
返回指针值的函数的一般定义形式为:
类型名 * 函数名(参数列表);
例如:
int *a(int a,int b)
a 是函数名,调用它后得到一个指向整型数据的指针(地址)。x 和 y 是函数 a 的形参, 为整型。
注意:在*a 的两侧没有括号;在 a 的两侧分别为*运算符和()运算符,由于()的优 先级高于*,因此 a 先于()结合。在函数前面有一个*,表示此函数是返回指针类型的函数。 最前面的 int 表示返回的指针指向整型变量。对初学 C++语言的人来说,这种定义形式可能 不太习惯,容易弄错,用时要十分小心。
【例 10】编写一个函数,用于在一个包含 N 个整数的数组中找到第一个质数,若有则返回 函数的地址;否则返回 NULL(空指针)。
1. #include<cstdio> 2. #include<cmath> 3. using namespace std; 4. int n,a[10001]; 5. bool isprime(int n)//判断素数 6. { 7. if(n<2) return false; 8. if(n==2) return true; 9. for(int i=2;i<=sqrt(n);++i){ 10. if(n%i==0) return false; 11. } 12. return true; 13. } 14. int* find() 15. { 16. for(int i=1;i<=n;++i) 17. if(isprime(a[i])) return &a[i];//或:return a+i; 18. return NULL;//找不到返回空指针 19. } 20. int main() 21. { 22. scanf("%d",&n); 23. for(int i=1;i<=n;++i) 24. scanf("%d",&a[i]); 25. int *p=find(); 26. if(p!=NULL) printf("%d\n%d\n",p,*p);//输出素数的地址及其本身 27. else 28. printf("Can't find!"); 29. return 0; 30. }
输入:
7
1 6 9 2 3 4 5
输出:
(可能是)4214864
2
函数指针和函数指针数组
一个指针变量通过指向不同的整数变量的地址,就可以对其他的变量操作。
程序中不仅数据是存放在内存空间中,代码也同样存放在内存空间里。具体讲,C++的 函数也保存在内存中,函数的入口地址也同样可以用指针访问。
另一方面,有些函数在编写时对要调用的辅助函数尚未确定,在执行时才能根据情况为其传递辅助函数的地址。比如 sort 函数的调用:“sort(a,a+n,cmp);”其中的比较函数 cmp 是我们根据需要转给 sort 的(也可能是 cmp1,cmp2 等),其实就是传递了函数指针。 下面我们来看一个具体例子。
【例 11】使用函数指针调用函数示例。
1. #include<iostream> 2. using namespace std; 3. int t(int a) 4. { 5. return a; 6. } 7. int main() 8. { 9. cout<<t<<endl; 10. cout<<(void*)t<<endl;//输出地址 11. printf("%x\n",t);//输出地址 12. int (*p)(int a);//定义函数指针变量p 13. p=t;//将函数t的地址赋给函数指针p 14. cout<<p(5)<<','<<(*p)(10)<<endl; 15. //输出 p(5)是 C++的写法,(*p)(10)是兼容 C,这是使用 p 来调用函数的两种方法 16. return 0; 17. }
输出:1
5,10
函数指针的基本操作有 3 个:
⑴声明函数指针。
声明要指定函数的返回类型以及函数的参数列表,和函数原型差不多。
例如,函数的原型是:int test(int);
相应的指针声明就是:int (*fp)(int);
要注意的是,不能写成:int *fp(int);
这样计算机编译程序会处理成声明一个 fp(int)的函数,返回类型是 int* 。
⑵获取函数的地址。
获取函数的地址很简单,只要使用函数名即可,例如,fp=test;
这表明函数名和数组名一样,可以看作是指针。
⑶使用函数指针来调用函数。
类似普通变量指针,可以用(*fp)来间接调用指向的函数。但 C++也允许像使用函数名一样使用 fp,虽然有争议,但C++确实是支持了。
函数指针还有另一种结合 typedef 的声明方式,如例 X 所示。
【例 12】使用 typedef 定义函数指针示例。
1. #include<iostream> 2. using namespace std; 3. int sum(int a,int b) 4. { 5. return a+b; 6. } 7. typedef int (*LP)(int,int);//定义了一个函数指针变量类型LP 8. int main() 9. { 10. LP p=sum;//定义了一个LP类型的函数指针p,并赋值为sum 11. cout<<p(2,5);//使用p来调用函数,实参为2和5,调用sum函数,返回7 12. return 0; 13. }
输出:7
在软件开发编程中,函数指针的一个广泛应用是菜单功能函数的调用。通常选择菜单的某个选项都会调用相应的功能函数,并且有些软件的菜单会根据情况发生变化。如果使用 swith/case 或 if 语句处理起来会比较复杂、冗长,不利于代码的维护,可以考虑使用函数指针数组方便灵活地实现。
【例 13】使用函数指针数组,模拟菜单功能实现方法示例
1. #include<iostream> 2. using namespace std; 3. void t1(){cout<<"test1";} 4. void t2(){cout<<"test2";} 5. void t3(){cout<<"test3";} 6. void t4(){cout<<"test4";} 7. void t5(){cout<<"test5";} 8. typedef void(*LP)();//定义了一个函数指针变量 9. int main() 10. { 11. LP a[]={t1,t2,t3,t4,t5};//定义了一个LP类型的函数指针数组a,并初始化 12. int x; 13. cin>>x; 14. a[x]();//使用a[x]()来调用选择的函数 15. return 0; 16. }
输入:2
输出:test3