learun开发社区 - 力软快速开发平台官方论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 331|回复: 6

C语言的那些小秘密之函数指针

  [复制链接]

2

主题

2

帖子

40

积分

新手上路

Rank: 1

积分
40
发表于 2019-4-12 11:03:00 | 显示全部楼层 |阅读模式

    我们经常会听到这样的说法,不懂得函数指针就不是真正的C语言高手。我们不管这句话对与否,但是它都从侧面反应出了函数指针的重要性,所以我们还是有必要掌握对函数指针的使用。先来看看函数指针的定义吧。
函数是由执行语句组成的指令序列或者代码,这些代码的有序集合根据其大小被分配到一定的内存空间中,这一片内存空间的起始地址就成为函数的地址,不同的函数有不同的函数地址,编译器通过函数名来索引函数的入口地址,为了方便操作类型属性相同的函数,c/c++引入了函数指针,函数指针就是指向代码入口地址的指针,是指向函数的指针变量。 因而“函数指针”本身首先应该是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整形变量、字符型、数组一样,这里是指向函数。C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是一致的。函数指针有两个用途:调用函数和做函数的参数。
数据类型标志符 (指针变量名) (形参列表);
“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如:
int function(int x,int y); /* 声明一个函数 */
int (*f) (int x,int y); /* 声明一个函数指针 */
f=function; /* 将function函数的首地址赋给指针f */
赋值时函数function不带括号,也不带参数,由于function代表函数的首地址,因此经过赋值以后,指针f就指向函数function(int x,int y);的代码的首地址。
下面的程序说明了函数指针调用函数的方法:
例一、
  1. #include
  2. int max ( int x, int y){ return x>y?x:y;}
  3. int min ( int x, int y){ return x
  4. void main ()
  5. { int ( *f ) ( int x, int y)=max;
  6. //f=&max;
  7. printf ( "%d,%d\t", max (2,6), (f)(5,4));
  8. f=min;
  9. printf (" %d,%d\t" , min (2,6), (f)(5,4));
  10. }
复制代码

注意:以上代码的红色部分我们将会在接下来的代码分析部分进行讲解,读者也可以思考下如果运行注释部分,结果是否还是正确的呢?
f是指向函数的指针变量,所以可把函数max()赋给f作为f的值,即把max()的入口地址赋给f,以后就可以用f来调用该函数,实际上f和max都指向同一个入口地址,不同就是f是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数。不过注意,指向函数的指针变量没有++和--运算,用时要小心。
函数括号中的形参可有可无,视情况而定,不过,在某些编译器中这是不能通过的。这个例子的补充如下。
  1. typedef int (*fun_ptr)(int,int);
  2. fun_ptr max_func=max;
复制代码

也就是说,赋给函数指针的函数应该和函数指针所指的函数原型是一致的。
例二、
  1. #include
  2. void FileFunc()
  3. {
  4. printf("FileFunc\n");
  5. }
  6. void EditFunc()
  7. {
  8. printf("EditFunc\n");
  9. }
  10. void main()
  11. {
  12. typedef void (*funcp)();
  13. funcp pfun= FileFunc;
  14. pfun();
  15. pfun = EditFunc;
  16. pfun();
  17. }
复制代码

看了上面两段代码,应该都知道如何用函数指针来调用函数了,但是我们刚刚在上面的描述中留下过一个问题,就是运行注释部分f=&max;结果是否还是正确的呢?下面我就给出上面两个运行结果的对别,然后来分析下原因。
对比以上的运行结果可以看出,f=&max语句被执行时的结果和没有被执行时的结果是一样的。为什么会出现这样的结果呢?答案是这是编译器处理的,max本身就是个地址,它没有放到任何变量里,自然没有取它的地址一说。所以我们可以看看在调试的过程中&max的值和max的值是一样的。调试代码如下:
  1. root@ubuntu:/home/shiyan# gdb ss
  2. GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
  3. Copyright (C) 2010 Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version 3 or later
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "i686-linux-gnu".
  9. For bug reporting instructions, please see:
  10. ...
  11. Reading symbols from /home/shiyan/ss...done.
  12. (gdb) list
复制代码

  1. 1 #include
  2. 2 int max ( int x, int y){ return x>y?x:y;}
  3. 3 int min ( int x, int y){ return x
  4. 4 void main ()
  5. 5 { int ( *f ) ( int x, int y)=max;
  6. 6 //f=&max;
  7. 7 printf ( "%d,%d\t", max (2,6), (f)(5,4));
  8. 8 f=min;
  9. 9 printf (" %d,%d\t" , min (2,6), (f)(5,4));
  10. 10 }
  11. (gdb) b 4
  12. Breakpoint 1 at 0x80483ec: file hanshu.c, line 4.
  13. (gdb) r
  14. Starting program: /home/shiyan/ss
  15. Breakpoint 1, main () at hanshu.c:5
  16. 5 { int ( *f ) ( int x, int y)=max;
  17. (gdb) print max
  18. $1 = {int (int, int)} 0x80483c4
  19. (gdb) print f
  20. $2 = (int (*)(int, int)) 0xbffff6c8
  21. (gdb) s
  22. 7 printf ( "%d,%d\t", max (2,6), (f)(5,4));
  23. (gdb)
  24. max (x=5, y=4) at hanshu.c:2
  25. 2 int max ( int x, int y){ return x>y?x:y;}
  26. (gdb) print max
  27. $3 = {int (int, int)} 0x80483c4
  28. (gdb) print &max
  29. $4 = (int (*)(int, int)) 0x80483c4
  30. (gdb) print *max
  31. $5 = {int (int, int)} 0x80483c4
  32. (gdb) s
  33. max (x=2, y=6) at hanshu.c:2
  34. 2 int max ( int x, int y){ return x>y?x:y;}
  35. (gdb) s
  36. main () at hanshu.c:8
  37. 8 f=min;
  38. (gdb) print min
  39. $6 = {int (int, int)} 0x80483d3
  40. (gdb) print &min
  41. $7 = (int (*)(int, int)) 0x80483d3
  42. (gdb) print *min
  43. $8 = {int (int, int)} 0x80483d3
  44. (gdb) s
  45. 9 printf (" %d,%d\t" , min (2,6), (f)(5,4));
  46. (gdb) print f
  47. $9 = (int (*)(int, int)) 0x80483d3
  48. (gdb) print &f
  49. $10 = (int (**)(int, int)) 0xbffff6ac
  50. (gdb) print *f
  51. $11 = {int (int, int)} 0x80483d3
  52. (gdb) s
  53. min (x=5, y=4) at hanshu.c:3
  54. 3 int min ( int x, int y){ return x
  55. (gdb) s
  56. min (x=2, y=6) at hanshu.c:3
  57. 3 int min ( int x, int y){ return x
  58. (gdb) print min
  59. $12 = {int (int, int)} 0x80483d3
  60. (gdb) s
  61. main () at hanshu.c:10
  62. 10 }
复制代码

在调试的过程中我print了很多的信息,细心的读者肯定能获得更多的收获,尤其是对变量f的print,读者可以自己阅读,学到更多的东西。我给出的只是一个参考的调试方式,希望读者能够举一反三,自己对代码进行实际的调试,加深理解。
上面说的都是用指针来实现函数的调用,接下来我们看一个用函数指针作为参数的用法。
  1. #include
  2. using namespace std;
  3. typedef int (*print)(int );
  4. int fun1(int i)
  5. {
  6. return (int)i;
  7. }
  8. void fun2(int j,print prt)
  9. {
  10. for(int k=0;k

  11. cout
复制代码

回复

使用道具 举报

0

主题

1

帖子

116

积分

注册会员

Rank: 2

积分
116
发表于 2019-4-12 11:47:20 | 显示全部楼层
写的不错!
回复

使用道具 举报

0

主题

3

帖子

95

积分

注册会员

Rank: 2

积分
95
发表于 2019-4-15 07:00:50 | 显示全部楼层
楼主高人啊,我先收藏了
回复

使用道具 举报

0

主题

5

帖子

248

积分

中级会员

Rank: 3Rank: 3

积分
248
发表于 2019-4-17 09:24:39 | 显示全部楼层
不错,不错,收藏了。
回复

使用道具 举报

0

主题

7

帖子

180

积分

注册会员

Rank: 2

积分
180
发表于 2019-4-17 22:28:42 | 显示全部楼层
哈 谢谢啦 !谢谢分享
回复

使用道具 举报

0

主题

1

帖子

52

积分

注册会员

Rank: 2

积分
52
发表于 2019-4-19 03:49:25 | 显示全部楼层
不错不错,值得学习啊!
回复

使用道具 举报

0

主题

5

帖子

16

积分

新手上路

Rank: 1

积分
16
发表于 2019-7-22 13:06:06 | 显示全部楼层
哈 谢谢啦 !谢谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|learun开发社区 - 力软快速开发平台官方论坛 ( 沪ICP备14034717号 )

GMT+8, 2020-4-8 03:43 , Processed in 0.202377 second(s), 24 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表