指针及其应用3——指针与函数

简介: 指针及其应用3——指针与函数

指针作为函数参数     指针可以作为函数的参数。

在函数章节中,我们把数字作为参数传入函数中,实际上就 是利用了传递指针(即传递数组的首地址)的方法。通过首地址,我们可以访问数组中的任 何一个元素。 对于指向其他类型变量的指针,我们可以用同样的方式处理。

例如,我们编写如下一个函数,用于将两个整型变量的值交换。

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

相关文章
|
18天前
|
存储 安全 C++
C++中的引用和指针:区别与应用
引用和指针在C++中都有其独特的优势和应用场景。引用更适合简洁、安全的代码,而指针提供了更大的灵活性和动态内存管理的能力。在实际编程中,根据需求选择适当的类型,能够编写出高效、可维护的代码。理解并正确使用这两种类型,是掌握C++编程的关键一步。
21 1
|
26天前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
6天前
|
编译器 C++
函数指针和函数对象不是同一类型怎么替换
函数指针和函数对象不是同一类型,为何可替换用作同一函数的参数
|
6天前
|
存储 C语言
C语言的函数返回值和指针
C|函数返回值(区分各类值)和指针(区分各类存储空间)的细节
|
2天前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
9 0
|
24天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
16 1
|
24天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
15 1
|
2天前
|
C语言
C语言中的函数指针、指针函数与函数回调
C语言中的函数指针、指针函数与函数回调
6 0
|
2天前
|
存储 C语言
C语言数组指针详解与应用
C语言数组指针详解与应用
10 0
|
28天前
|
C++ 存储 Java
C++ 引用和指针:内存地址、创建方法及应用解析
'markdown'C++ 中的引用是现有变量的别名,用 `&` 创建。例如:`string &meal = food;`。指针通过 `&` 获取变量内存地址,用 `*` 创建。指针变量存储地址,如 `string *ptr = &food;`。引用不可为空且不可变,指针可为空且可变,适用于动态内存和复杂数据结构。两者在函数参数传递和效率提升方面各有优势。 ```