zoukankan      html  css  js  c++  java
  • C/C++的函数名和函数指针的关系剖析

       博客的处女篇啊!一直在回头看C++的知识,这几天就和指针耗上了!说实话现在上次仔细看C++的时候还是两年前了!后头再看发现真越看问题越多,倒不是我知识退步了,而是对问题更加的求甚解了!不是有人说过么:读书能读出问题,才没有白读!(应该不是我自己发明的)

           接下来的几篇文章准备写一写C++的指针和引用方面的东西吧,倒不是来给大家讲课,只是分享,学习,全当自己做个笔记,也欢迎各路神仙小妖多多交流指导!废话少说,直奔主题!

           今天的Topic就是函数名指针和函数指针:我们先来看一下函数指针式如何定义的,假如我们有一个函数int fun(int){...};那么他对应的函数指针写法就应该是int (*p)(int);然后再对他进行赋值,即p=fun;之后你就可以在接下来的地方按p作为函数名来调用它用起来完全和fun一样。(注意这里的p指针并不是只能接受fun这个函数名,任何返回值是int,参数只有一个int的函数都可以把函数名赋给p)

      先陪女朋友出去吃个饭,回来继续写哈。。。

      上面都是些基本知识,大家都应该知道,下面就分享一下我个人对网上各种参考资料的总结和自我理解(仅供参考):首先说一下C/C++在创建一个变量的时候比如int a;相应的在内存就会分配一个4个字节(根据不同机器可能不同)空间来存放这个int变量,而假设这4个字节的起始地址是0XFF0A,那么实际上就存在一种变量名和内存地址的映射,即a可以看做是一个标示符,他只是代表着0XFF0A这个地址,在程序中你对a进行的操作实际上也就是对内存中以0XFF0A为首地址的4个字节的操作,特别是如果对a进行取地址操作也就是&a实际上就是返回0XFF0A这个地址值,实际上你可以看成就是返回一个指向这个地址的指针(如果你觉的不能理解,就当我没说吧).同理对于我们在程序中创建的函数,他是保存在程序中的单独区域的,而我们调用它们就像使用变量一样需要一个地址来唯一的指向它,所以每个函数都需要一个地址来唯一标识自己(也就是我们常说的入口地址),就像上面的a对应0XFF0A,那么假设我们定义了一个int fun(int){};函数的入口地址是0XAAEE,则fun也就是函数名他会映射0XAAEE,和上面的int变量a一样如果对它进行取地址&fun的话就会返回0XAAEE,实际上fun也是一种类型,就当它是函数名类型好了,只要记住函数名本身并不是一个指针类型就可以了。

      下面说重点,我们都知道在调用函数的时候有函数名就够了,比如fun(2);不要以为只要有函数名就能调用函数了,其实这只是写法上的一个迷惑点,而编译器在编译的时候一律都会进行所谓的"Function-to-pointer conversion",也就是把函数名隐式转换成函数指针类型,也就是要通过函数指针来调用函数,所以如果你在调用函数的时候写成(&fun)(2)也是一样能工作的,因为&fun实际上就是返回一个函数指针,参照上一段中&a的例子,只是这种写法很不常见,即使你不显式的写出&的话编译器也会隐式的进行转换,注意&fun左右的括号必须有,这是因为运算符优先级的问题。

      其实即使你写成(*fun)(2)也是可以正常运行的,这是因为当编译器看到fun的时候发现它前面没有&也就是你没有给他显示的转换成指针那么他就要隐式的转换成指针,当转换完之后发现前面又有一个*这时候也就是要进行所谓的"解引用"操作,也就是到*后面里指针里取出来值,而那么值实际上也就也就是0XAAEE也就是函数名fun,这么一次隐式换然后再来一次解引用实际上相当于什么也没做,所以系统还会再进行一次隐式的"Function-to-pointer conversion",即使你写成(*******fun)(2)也会正常运行,和刚才的一个道理,只是多做了几次反复的转解操作而已,都是编译器自己完成的,不必去理会!

      上面的文字叙述可能有些啰嗦,不知道大家能不能听懂我的意思。接下来上几行代码,大家结合着看看

     1 #include<iostream>
     2 using namespace std;
     3 void fun(int a)
     4 {
     5     
     6 }
     7 
     8 int main()
     9 {
    10     cout<<fun<<endl;
    11     cout<<*fun<<endl;
    12     cout<<&fun<<endl;
    13     cout<<*****fun<<endl;
    14 }

    你在自己机器上运行一下你会发现他们输出的值都是一样的,也就是都是指向同一个函数地址的指针值。

    下面我们再结合自己定义的函数指针来看看

     1 #include<iostream>
     2 using namespace std;
     3 int fun(int a)
     4 {
     5     cout<<"fun"<<endl;
     6     return 0;
     7 }
     8 
     9 void main()
    10 {
    11     int(*p)(int)=fun;
    12     int(*p1)(int)=*fun;
    13     int(*p2)(int)=&fun;
    14     p(1);
    15     p1(1);
    16     p2(1);
    17 }

    你会发现函数都能正常的运行,其实p1,p2,p和fun赋值之后大家一样理解就行了。接着上代码

    #include<iostream>
    using namespace std;
    int fun(int a)
    {
    	cout<<"fun"<<endl;
    	return 0;
    }
    
    void main()
    {
    	int(*p)(int)=fun;
    	p(1);
    //	(&p)(1);    
    	(*p)(1);
    	(****p)(1);
    }
    

    上面的程序也都会正常的运行,只要再理解的时候把p当成只是对函数名多做了一次转换就可以了,接下来理解都一样!注意上面注释掉的哪一行是不能运行的,因为p是我们自己定义的函数指针类型,如果你对指针取地址那么将得到p这个变量本身的地址,这就不能正确调用函数了!再多说一句,其实你如果运行&&fun这个式子也是非法的,至于为什么,大家一起帮我思考思考,我个人认为当我们运行&fun的时候他会转换成函数指针而实际上这个指针只是一个临时值而临时值是没有实际存放的内存地址的所以也就无法继续取地址了!个人猜想,大家一起帮我分析分析吧!

      呵呵,第一个博文算是写完了,也该睡觉了!很久没写东西了,有点生疏了,希望大家多多见谅,以后再接再厉和大家一起分享,一起学习,上述内容很多都加入了笔者自己的理解,可能有不对的地方,希望大家不吝指出,谢谢了!

      如需转载,希望大家注明原文出处链接以及说明作者Esfog,谢谢,分享是一种快乐!分享是一种态度!

    http://www.cnblogs.com/Esfog/archive/2012/04/23/2467249.html

      

  • 相关阅读:
    WPF 使用 Direct2D1 画图 绘制基本图形
    WPF 使用 Direct2D1 画图 绘制基本图形
    dot net core 使用 IPC 进程通信
    dot net core 使用 IPC 进程通信
    win2d 图片水印
    win2d 图片水印
    Java实现 LeetCode 240 搜索二维矩阵 II(二)
    PHP closedir() 函数
    PHP chroot() 函数
    PHP chdir() 函数
  • 原文地址:https://www.cnblogs.com/Esfog/p/2467249.html
Copyright © 2011-2022 走看看