当前位置: 首页 > 科技观察

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

时间:2023-03-21 13:47:16 科技观察

函数就是一个指令序列或者由执行语句组成的代码。这些代码的有序集合,按照其大小分配到一定的内存空间。这个内存空间的起始地址就成了函数的地址。不同的函数有不同的函数地址。编译器使用函数名来索引函数的入口地址。为了方便具有相同类型属性的函数的运行,c/c++引入了函数指针,指向代码入口。地址的指针是指向函数的指针变量。因此,“函数指针”本身首先应该是一个指针变量,只不过指针变量指向的是一个函数。这就好比用一个指针变量来指向一个整型变量、一个字符类型或者一个数组,这里是指向一个函数。C编译时,每个函数都有一个入口地址,也就是函数指针指向的地址。有了指向函数的指针变量后,就可以用指针变量来调用函数了,就像指针变量可以引用其他类型的变量一样,这些概念是一致的。函数指针有两个用途:调用函数和作为函数的参数。函数指针的声明方式为:数据类型标识符(指针变量名)(形参列表);“函数类型”表示函数的返回类型,由于“”的优先级高于“*”,所以指针变量名括号是必不可少的,后面的“形参列表”表示所指向的函数的参数列表通过指针变量。例如:intfunction(intx,inty);/*声明一个函数*/int(*f)(intx,inty);/*声明一个函数指针*/f=function;/*将函数function的首地址赋值给指针f*/赋值时,function函数没有括号和参数。由于function表示函数的首地址,赋值后,指针f指向函数function(intx,inty)代码的首地址;下面的程序说明了用函数指针调用函数的方法:例一,#includeintmax(intx,inty){returnx>y?x:y;}intmin(intx,inty){returnxvoidmain{int(*f)(intx,inty))=max;//f=&max;printf("%d,%d\t",max(2,6),(f)(5,4));f=min;printf("%d,%d\t",min(2,6),(f)(5,4));}注:上面代码中红色部分会在接下来的代码分析中解释,读者可以还想着怎么跑评论区,结果还对吗?f是一个指向函数的指针变量,所以可以把函数max赋给f作为f的值,也就是把max的入口地址赋给f,然后就可以用f来调用函数了,其实,f和max都指向同一个入口地址,不同的是f是一个指针变量,不像函数名那样是死的,它可以指向任何函数,就看你要干什么了。把程序中任意一个函数的地址赋给它,它就会指向那个函数。然后用一个指针变量调用它,这样就可以依次指向不同的函数。但是要注意,指向函数的指针变量没有++和--操作,所以使用时要小心。函数括号中的形式参数是可选的,视情况而定,但在某些编译器中不能传递。本例补充如下。1.定义函数指针类型:typedefint(*fun_ptr)(int,int);2、声明变量并赋值:fun_ptrmax_func=max;也就是说,分配给函数指针的函数应该和函数指针指向的函数原型一致。例子2,#includevoidFileFunc{printf("FileFunc\n");}voidEditFunc{printf("EditFunc\n");}voidmain{typedefvoid(*funcp);funcppfun=FileFunc;pfun;pfun=EditFunc;pfun;}见看完上面两段代码,你应该知道如何使用函数指针来调用函数了,但是我们刚才在上面的描述中留下了一个疑问,就是运行注释部分f=&max;的结果。还是正确的?接下来我会给出上面两个运行结果的区别,然后分析原因。添加注释部分的运行结果为:对比上面的运行结果可以看出,f=&max语句执行时的结果与不执行时的结果是一样的。为什么会有这样的结果呢?答案是这是由编译器处理的。max本身就是一个地址,并没有放在任何变量中。自然地,它的地址没有被占用。所以我们在调试的时候可以看到&max的值和max的值是一样的。测试代码如下:root@ubuntu:/home/shiyan#gdbssGNUgdb(Ubuntu/Linaro7.2-1ubuntu11)7.2Copyright(C)2010FreeSoftwareFoundation,Inc.LicenseGPLv3+:GNUGPLversion3orlaterThisisfreesoftware:youarefreetochangeandredistributeit.ThereisNOWARRANTY"showwarranty"showwarrantyandtotheextentpermitted对于详细信息。此GDB配置为“i686-linux-gnu”。有关错误报告说明,请参阅:...从/home/shiyan/ss中读取符号...完成。(gdb)list1#include2intmax(intx,inty){returnx>y?x:y;}3intmin(intx,inty){returnx4voidmain5{int(*f)(intx,inty)=max;6//f=&max;7printf("%d,%d\t",max(2,6),(f)(5,4));8f=min;9printf("%d,%d\t",min(2,6),(f)(5,4));10}(gdb)b4Breakpoint1at0x80483ec:filehanshu.c,line4.(gdb)rStartingprogram:/home/shiyan/ssBreakpoint1,mainathanshu.c:5(gdb)printmax$1={int(int,int)}0x80483c4(gdb)printf$2=(int(*)(int,int))0xbffff6c8(gdb)s(gdb)max(x=5,y=4)athanshu.c:22intmax(intx,inty){returnx>y?x:y;}(gdb)printmax$3={int(int,int)}0x80483c4(gdb)print&max$4=(int(*)(int,int))0x80483c4(gdb)print*max$5={int(int,int)}0x80483c4(gdb)smax(x=2,y=6)athanshu.c:2(gdb)smainathanshu.c:88f=min;(gdb)printmin$6={int(int,int)}0x80483d3(gdb)print&min$7=(int(*)(int,int))0x80483d3(gdb)print*min$8={int(int,int)}0x80483d3(gdb)s9printf("%d,%d\t",min(2,6),(f)(5,4));(gdb)printf$9=(int(*)(int,int))0x80483d3(gdb)print&f$10=(int(**)(int,int))0xbffff6ac(gdb)print*f$11={int(int,int)}0x80483d3(gdb)smin(x=5,y=4)athanshu.c:33intmin(intx,inty){returnx(gdb)smin(x=2,y=6)athanshu.c:3(gdb)printmin$12={int(int,int)}0x80483d3(gdb)smainathanshu.c:1010}调试过程中打印了很多信息,细心的读者一定会有更多收获,尤其是对于变量f的打印,读者可以自行阅读,了解更多。我给出的只是一个参考调试方法。希望读者举一反三,自己实际调试代码,加深理解。以上都是使用指针来实现函数调用。接下来我们看一个使用函数指针作为参数的用法。#includeusingnamespacestd;typedefint(*print)(int);intfun1(inti){return(int)i;}voidfun2(intj,printprt){for(intk=0;kcout<<'\t'<}voidmain{inti=10;fun2(i,fun1);}运行结果如下:看了上面的描述,我想我对函数指针的概念有了一个大概的了解,还有一个希望大家不要混淆的概念就是指针函数.这两个概念是缩写,指针函数是指带指针的函数,也就是本质上是一个函数,我们知道函数是有返回类型的(如果不返回值就是无值的,就是void),但指针函数的返回类型是某种类型的指针,它的定义格式如下:返回类型标识符*返回名称(形参表){函数体}返回类型可以是任何基本类型和复合类型。返回指针的函数应用非常广泛。其实每一个函数,即使不返回某种类型的指针,本身也h作为入口地址,相当于一个指针。比如一个返回整数值的函数,其实就相当于返回了一个指针变量的值,只不过此时的变量是函数本身,整个函数就相当于一个“变量”。函数的返回值我会在下一章讲解,本章到此结束。希望以上内容对您有所帮助!C语言博大精深。由于本人水平有限,博客中难免有不足或错误之处。恳请广大读者批评指正。同时也欢迎广大读者共同探讨相关内容。如果您愿意交流,请留下您宝贵的意见。