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

简介:

函数是由执行语句组成的指令序列或者代码,这些代码的有序集合根据其大小被分配到一定的内存空间中,这一片内存空间的起始地址就成为函数的地址,不同的函数有不同的函数地址,编译器通过函数名来索引函数的入口地址,为了方便操作类型属性相同的函数,c/c++引入了函数指针,函数指针就是指向代码入口地址的指针,是指向函数的指针变量。 因而“函数指针”本身首先应该是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整形变量、字符型、数组一样,这里是指向函数。C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是一致的。函数指针有两个用途:调用函数和做函数的参数。

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

函数指针的声明方法为:

数据类型标志符 (指针变量名) (形参列表);

“函数类型”说明函数的返回类型,由于“”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如:

 
  1. int function(int x,int y); /* 声明一个函数 */ 
  2. int (*f) (int x,int y); /* 声明一个函数指针 */ 

f=function; /* 将function函数的首地址赋给指针f */

赋值时函数function不带括号,也不带参数,由于function代表函数的首地址,因此经过赋值以后,指针f就指向函数function(int x,int y);的代码的首地址。

下面的程序说明了函数指针调用函数的方法:

例一、

 
  1. #include 
  2.  
  3. int max ( int x, int y){ return x>y?x:y;} 
  4.  
  5. int min ( int x, int y){ return x 
  6.  
  7. void main 
  8.  
  9. int ( *f ) ( int x, int y)=max; 
  10.  
  11. //f=&max; 
  12.  
  13. printf ( "%d,%d\t", max (2,6), (f)(5,4)); 
  14.  
  15. f=min; 
  16.  
  17. printf (" %d,%d\t" , min (2,6), (f)(5,4)); 
  18.  

注意:以上代码的红色部分我们将会在接下来的代码分析部分进行讲解,读者也可以思考下如果运行注释部分,结果是否还是正确的呢?

f是指向函数的指针变量,所以可把函数max赋给f作为f的值,即把max的入口地址赋给f,以后就可以用f来调用该函数,实际上f和max都指向同一个入口地址,不同就是f是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数。不过注意,指向函数的指针变量没有++和--运算,用时要小心。

函数括号中的形参可有可无,视情况而定,不过,在某些编译器中这是不能通过的。这个例子的补充如下。

1.定义函数指针类型:

 
  1. typedef int (*fun_ptr)(int,int); 

2.申明变量,赋值:

 
  1. fun_ptr max_func=max; 

也就是说,赋给函数指针的函数应该和函数指针所指的函数原型是一致的。

例二、

 
  1. #include 
  2.  
  3. void FileFunc 
  4.  
  5.  
  6. printf("FileFunc\n"); 
  7.  
  8.  
  9. void EditFunc 
  10.  
  11.  
  12. printf("EditFunc\n"); 
  13.  
  14.  
  15. void main 
  16.  
  17.  
  18. typedef void (*funcp); 
  19.  
  20. funcp pfun= FileFunc; 
  21.  
  22. pfun; 
  23.  
  24. pfun = EditFunc; 
  25.  
  26. pfun; 
  27.  

看了上面两段代码,应该都知道如何用函数指针来调用函数了,但是我们刚刚在上面的描述中留下过一个问题,就是运行注释部分f=&max;结果是否还是正确的呢?下面我就给出上面两个运行结果的对别,然后来分析下原因。

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

把注释部分加进去的运行结果为:

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

对比以上的运行结果可以看出,f=&max语句被执行时的结果和没有被执行时的结果是一样的。为什么会出现这样的结果呢?答案是这是编译器处理的,max本身就是个地址,它没有放到任何变量里,自然没有取它的地址一说。所以我们可以看看在调试的过程中&max的值和max的值是一样的。调试代码如下:

 

  1. root@ubuntu:/home/shiyan# gdb ss 
  2.  
  3. GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2 
  4.  
  5. Copyright (C) 2010 Free Software Foundation, Inc. 
  6.  
  7. License GPLv3+: GNU GPL version 3 or later 
  8.  
  9. This is free software: you are free to change and redistribute it. 
  10.  
  11. There is NO WARRANTY, to the extent permitted by law. Type "show copying" 
  12.  
  13. and "show warranty" for details. 
  14.  
  15. This GDB was configured as "i686-linux-gnu"
  16.  
  17. For bug reporting instructions, please see: 
  18.  
  19. ... 
  20.  
  21. Reading symbols from /home/shiyan/ss...done. 
  22.  
  23. (gdb) list 
  24.  
  25. 1 #include 
  26.  
  27. 2 int max ( int x, int y){ return x>y?x:y;} 
  28.  
  29. 3 int min ( int x, int y){ return x 
  30.  
  31. 4 void main 
  32.  
  33. 5 { int ( *f ) ( int x, int y)=max; 
  34.  
  35. 6 //f=&max; 
  36.  
  37. 7 printf ( "%d,%d\t", max (2,6), (f)(5,4)); 
  38.  
  39. 8 f=min; 
  40.  
  41. 9 printf (" %d,%d\t" , min (2,6), (f)(5,4)); 
  42.  
  43. 10 } 
  44.  
  45. (gdb) b 4 
  46.  
  47. Breakpoint 1 at 0x80483ec: file hanshu.c, line 4
  48.  
  49. (gdb) r 
  50.  
  51. Starting program: /home/shiyan/ss 
  52.  
  53. Breakpoint 1, main at hanshu.c:5 
  54.  
  55. (gdb) print max 
  56.  
  57. $1 = {int (intint)} 0x80483c4 
  58.  
  59. (gdb) print f 
  60.  
  61. $2 = (int (*)(intint)) 0xbffff6c8 
  62.  
  63. (gdb) s 
  64.  
  65. (gdb) 
  66.  
  67. max (x=5, y=4) at hanshu.c:2 
  68.  
  69. 2 int max ( int x, int y){ return x>y?x:y;} 
  70.  
  71. (gdb) print max 
  72.  
  73. $3 = {int (intint)} 0x80483c4 
  74.  
  75. (gdb) print &max 
  76.  
  77. $4 = (int (*)(intint)) 0x80483c4 
  78.  
  79. (gdb) print *max 
  80.  
  81. $5 = {int (intint)} 0x80483c4 
  82.  
  83. (gdb) s 
  84.  
  85. max (x=2, y=6) at hanshu.c:2 
  86.  
  87. (gdb) s 
  88.  
  89. main at hanshu.c:8 
  90.  
  91. 8 f=min; 
  92.  
  93. (gdb) print min 
  94.  
  95. $6 = {int (intint)} 0x80483d3 
  96.  
  97. (gdb) print &min 
  98.  
  99. $7 = (int (*)(intint)) 0x80483d3 
  100.  
  101. (gdb) print *min 
  102.  
  103. $8 = {int (intint)} 0x80483d3 
  104.  
  105. (gdb) s 
  106.  
  107. 9 printf (" %d,%d\t" , min (2,6), (f)(5,4)); 
  108.  
  109. (gdb) print f 
  110.  
  111. $9 = (int (*)(intint)) 0x80483d3 
  112.  
  113. (gdb) print &f 
  114.  
  115. $10 = (int (**)(intint)) 0xbffff6ac 
  116.  
  117. (gdb) print *f 
  118.  
  119. $11 = {int (intint)} 0x80483d3 
  120.  
  121. (gdb) s 
  122.  
  123. min (x=5, y=4) at hanshu.c:3 
  124.  
  125. 3 int min ( int x, int y){ return x 
  126.  
  127. (gdb) s 
  128.  
  129. min (x=2, y=6) at hanshu.c:3 
  130.  
  131. (gdb) print min 
  132.  
  133. $12 = {int (intint)} 0x80483d3 
  134.  
  135. (gdb) s 
  136.  
  137. main at hanshu.c:10 
  138.  
  139. 10 } 

在调试的过程中我print了很多的信息,细心的读者肯定能获得更多的收获,尤其是对变量f的print,读者可以自己阅读,学到更多的东西。我给出的只是一个参考的调试方式,希望读者能够举一反三,自己对代码进行实际的调试,加深理解。

上面说的都是用指针来实现函数的调用,接下来我们看一个用函数指针作为参数的用法。

 
  1. #include 
  2.  
  3. using namespace std; 
  4.  
  5. typedef int (*print)(int ); 
  6.  
  7. int fun1(int i) 
  8.  
  9.  
  10. return (int)i; 
  11.  
  12.  
  13. void fun2(int j,print prt) 
  14.  
  15.  
  16. for(int k=0;k 
  17.  
  18. cout<<'\t'
  19.  
  20.  
  21. void main 
  22.  
  23.  
  24. int i=10; 
  25.  
  26. fun2(i,fun1); 
  27.  

运行结果如下:

看了上面的描述,我想都对函数指针的概念有了大致的了解,另外一个希望大家不要混淆的概念就是指针函数,,这两个概念都是简称,指针函数是指带指针的函数,即本质是一个函数。我们知道函数都又有返回类型(如果不返回值,则为无值型,即为void),只不过指针函数返回类型是某一类型的指针。

其定义格式如下所示:

返回类型标识符 *返回名称(形式参数表)

{ 函数体}

返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”,关于函数的返回值问题我将在下一章来讲解,本章到此为止。希望以上内容对你有所帮助!

C语言博大精深,由于本人水平有限,博客中的不妥或错误之处在所难免,殷切希望读者批评指正。同时也欢迎读者共同探讨相关的内容,如果乐意交流的话请留下你宝贵的意见。


来源:51CTO

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
1月前
|
存储 编译器 C语言
[C语言] 初识指针
[C语言] 初识指针
|
8天前
|
存储 C语言
C语言 — 指针进阶篇(下)
C语言 — 指针进阶篇(下)
15 0
|
8天前
|
存储 C语言 C++
C语言 — 指针进阶篇(上)
C语言 — 指针进阶篇(上)
6 0
|
15天前
|
存储 C语言
【C语言】深入解开指针(三)2
【C语言】深入解开指针(三)
|
15天前
|
存储 程序员 C语言
【C语言】深入解开指针(二)2
【C语言】深入解开指针(二)
【C语言】深入解开指针(二)2
|
15天前
|
存储 C语言
【C语言】深入解开指针(一)1
【C语言】深入解开指针(一)
|
16天前
|
C语言 索引
基于C语言的函数指针应用-消息命令处理框架
基于C语言的函数指针应用-消息命令处理框架
11 0
|
23天前
|
存储 人工智能 编译器
C语言指针详解
指针运算,指针和数组,二级指针
C语言指针详解
|
23天前
|
存储 C语言
C语言第二十四弹---指针(八)
C语言第二十四弹---指针(八)
|
23天前
|
编译器 C语言
C语言第十九弹---指针(三)
C语言第十九弹---指针(三)