C语言-函数指针与函数名的区别

来源:互联网 发布:手机淘宝直播怎么开通 编辑:程序博客网 时间:2024/04/30 05:45

    记得大学时老师曾说函数的函数名是函数的入口的指针,之前看block通过clang编译生成的C代码发现很多函数指针,于是想了解函数指针与函数名有什么区别?以及函数指针一般都有些什么作用。

函数指针与函数名的区别

首先先定义一函数以及一个指向盖函数的函数指针,并分别对他们进行调用。

[objc] view plain copy
  1. #include <stdio.h>  
  2. void fun(int x);  
  3. int main(int argc, const charchar * argv[]) {  
  4.     void (*funP)(int);//声明函数指针funP  
  5.     funP = &fun;  
  6.       
  7.     fun(1);  
  8.     (*funP)(2);  
  9.       
  10.     (* fun)(3);  
  11.     funP(4);  
  12.     return 0;  
  13. }  
  14. //定义函数fun  
  15. void fun(int x){  
  16.     printf("%i\n",x);  
  17. }  
输出结果为
[plain] view plain copy
  1. 1  
  2. 2  
  3. 3  
  4. 4  
    正如我们平常使用的那样,1如何2正常输出了。为了确认函数名是否等价于函数指针,于是把函数名以及函数指针的调用方式换了一下。发现(* <函数名>)()的的形式也能进行调用并正常输出,而函数指针直接调用也没有问题。

    这里暂时得出两个结论

    1、函数名的使用基本等价于函数指针。

    2、函数名也可以(* <函数名>)()来调用,只是这种方法读写都不方便,所以被简化了。

    得到一个问题是:为什么使用“funP = &fun”的形式对funP赋值,而不直接使用“funP = fun”

对于上面得出的问题,我们试着直接输出funP与fun作为指针的值,进行比较。代码如下

[objc] view plain copy
  1. <span style="font-size:14px;">#include <stdio.h>  
  2. void fun(int x);  
  3. int main(int argc, const charchar * argv[]) {  
  4.     void (*funP)(int);  
  5.     funP = &fun;  
  6.       
  7.     printf("%p\n",&fun);  
  8.     printf("%p\n\n",funP);  
  9.       
  10.     printf("%p\n",fun);  
  11.     printf("%p\n\n",&funP);  
  12.     return 0;  
  13. }  
  14. //函数定义  
  15. void fun(int x){  
  16.     printf("%i\n",x);  
  17. }</span>  
输出结果为:

[plain] view plain copy
  1. 0x100000f40  
  2. 0x100000f40  
  3.   
  4. 0x100000f40  
  5. 0x7fff5fbff888  
  6.   
  7. Program ended with exit code: 0  
       首先,所有结果都正常打印了(似乎fun真的像一个指针)。

       其次在打印fun作为指针的内容时,我们发现fun(暂时看做是一个指针)的内容就是它的地址,fun是一个指向自己的指针。根据大家常说的fun作为函数的入口的依据,那么它的地址就是函数入口的地址,其次我们通过fun来找到函数,那么他就应该指向函数的入口。这么解释似乎能说的通为什么fun是一个指向自己的指针。虽然感觉有点怪怪的。

       经过上面两端代码后我们好像能确定函数名就是函数指针,那我们看看作为函数指针,它能否做其他函数指针能做的事,比如赋值。

[objc] view plain copy
  1. funP = &fun;  
  2. (*funP)(1);  
  3. funP = fun;  
  4. (*funP)(2);  
       首先对函数指针funP进行两种赋值,发现都能同通过编译并运行。

       其次对fun进行赋值。发现下面两条都无法通过编译。

[objc] view plain copy
  1. fun = funP;  
[objc] view plain copy
  1. fun = &funp;  

由此我们发现函数名作为指针无法被赋值。对此,在网上发现两种解释

第一种:函数名与FunP函数指针都是函数指针。fun是一个函数指针常量,funP是一个函数数指针变量。

        虽然通过常量与变量来解释函数名无法赋值可以帮助理解,但是我们发现对fun赋值时编译器给的错误提示并不是说对常量进行赋值,而是告诉我们=号两端格式不匹配。对此,第二种理解更合理。


第二种:函数名和数组名实际上都不是指针,但是,在使用时可以退化成指针,即编译器可以帮助我们实现自动的转换。

        这也可以解释为什么当我们在=号右侧使用函数名时,无论是取值还是取地址都没有问题,因为编译替我们做了相当于强制类型转换的工作,而在当函数名在=号左侧时,右侧的函数指针并没有这个功能,毕竟他们俩不是同一种结构。

函数指针的作用

           当我无法区别函数名与函数指针时,我很好奇既然函数名也是函数指针类型,那为什么不直接使用函数名。提出函数指针的目的是什么,它有什么作用。虽然现在明白了函数名不等于函数指针,但是问题还是要解决。

1、作为变量传递,可称为参数

       既然函数指针如同别的指针变量一样通过*来获得,那么函数指针作为变量,自然可以进行赋值,取值操作,可以作为函数的参数进行传递。普通指针变量能做什么它就能做什么。

2、优化函数调用,封装

      通常函数名的命名都是见名知意,直接用函数名调用可读性自然要好,但如果是不想给别人查看的代码,被人破解后通过函数名直接就了解具体函数作用与函数调用,而函数指针可以作为函数的一层外衣,提供一定的保护作用

      其次通过函数指针来调用函数,可以起到一定的封装效果,函数指针作为引用层(中层),函数作为实现层(底层),便于分层设计,函数指针可以为上层用户提供统一借口,便于系统抽象各个功能或操作,降低程序耦合度。如

<span style="font-size: 18px;">fopen就是个例子,他可以打开文件。而C里面将磁盘文件、串口、USB等诸多设备抽象为文件。为C++实现多态性的虚函数表也是通过函数指针实现。</span>
<span style="font-size: 18px;">3、回调函数</span>
<span style="font-size: 18px;">      这是最重要的使用场景了,回调函数就是一个通过函数指针调用的函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。举个简单的例子。如去干洗店洗衣服,我们通常会留下电话号码,而干洗店洗好衣服后就会通过电话通知我们,让我们来取衣服。这个场景里电话号码就是回调函数,相当于函数指针。这在实现通知机制的时候就能看到。其次在我们编写一个对一般数据类型进行操作的库时,为了能让库可用于多种数据类型(int、float、string),也可以使用函数指针,并进行回调。</span>
<span style="font-size: 18px;"></span>
<span style="font-size: 18px;">回调函数的具体使用例子:</span>
<span style="font-size: 18px;">http://blog.csdn.net/callmeback/article/details/4242260</span>
<span style="font-size:18px;"></span>
<span style="font-size: 18px;"></span>