原文地址:http://blog.csdn.net/qingshuiyangfan/article/details/7692647

学习要点:

1,函数地址的一般定义和typedef简化定义;
        2,函数地址的获取;
        3,A函数地址作为B函数参数的传递;
    函数存放在内存的代码区域内,它们同样有地址.如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同
数组一样,数组的名字就是数组的起始地址。
    定义一个指向函数的指针用如下的形式,以上面的test()为例:
    int (*fp)(int a);//这里就定义了一个指向函数(这个函数的参数仅仅为一个int类型)的指针
    一般定义方式:
    data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);
    函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,这点尤其注意.
    在定义函数指针的时候我们很容易犯如下的错误:
    int *fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整形指针的函数了,而不是函数指针.
    下面我们来看一个具体的例子:

    #include <iostream>
    #include <string>
    using namespace std;

int test(int a);
 
    void main(int argc,char* argv[])   
    {
        cout<<test<<endl;//显示函数地址
        int (*fp)(int a);
        fp=test;//将函数test的地址赋给函数学指针fp
        cout<<fp(5)<<"|"<<(*fp)(10)<<endl;
        //上面的输出fp(5),这是标准c++的写法,(*fp)(10)这是兼容c语言的标准写法,两种同意,但注意区分,避免写的程序产生移植性问题!
        cin.get();
    }
 
    int test(int a)
    {
        return a;
    }

typedef定义可以简化函数指针的定义,在定义一个的时候感觉不出来,但定义多了就知道方便了,上面的代码改写成如下的形式:

    #include <iostream>
    #include <string>
    using namespace std;
 
    int test(int a);
 
    void main(int argc,char* argv[])   
    {
        cout<<test<<endl;
        typedef int (*fp)(int a);//注意,这里不是生命函数指针,而是定义一个函数指针的类型,这个类型是自己定义的,类型名为fp
        fp fpi;//这里利用自己定义的类型名fp定义了一个fpi的函数指针!
        fpi=test;
        cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl;
        cin.get();
    }
 
    int test(int a)
    {
        return a;
    } 

函数指针同样是可以作为参数传递给函数的,下面我们看个例子,仔细阅读你将会发现它的用处,稍加推理可以很方便使我们进行一些复杂的编程工作。

    //-------------------该例以上一个例子作为基础稍加了修改-----------------------------
    #include <iostream>   
    #include <string>   
    using namespace std;   
   
    int test(int);   
 
    int test2(int (*ra)(int),int);
 
    void main(int argc,char* argv[])     
    {   
        cout<<test<<endl;
        typedef int (*fp)(int);   
        fp fpi;
        fpi=test;//fpi赋予test 函数的内存地址
 
        cout<<test2(fpi,1)<<endl;//这里调用test2函数的时候,这里把fpi所存储的函数地址(test的函数地址)传递了给test2的第一个形参
        cin.get();
    }   
   
    int test(int a)
    {   
        return a-1;
    }
 
    int test2(int (*ra)(int),int b)//这里定义了一个名字为ra的函数指针
    {
        int c=ra(10)+b;//在调用之后,ra已经指向fpi所指向的函数地址即test函数
        return c;
    }

利用函数指针,我们可以构成指针数组,更明确点的说法是构成指向函数的指针数组,这么说可能就容易理解的多了。

    #include <iostream>   
    #include <string>   
    using namespace std;
 
    void t1(){cout<<"test1";}
    void t2(){cout<<"test2";}
    void t3(){cout<<"test3";}
    void main(int argc,char* argv[])     
    {
        void* a[]={t1,t2,t3};
        cout<<"比较t1()的内存地址和数组a[0]所存储的地址是否一致"<<t1<<"|"<<a[0]<<endl;
 
        cout<<a[0]();//错误!指针数组是不能利用数组下标操作调用函数的
 
        typedef void (*fp)();//自定义一个函数指针类型
        fp b[]={t1,t2,t3}; //利用自定义类型fp把b[]定义趁一个指向函数的指针数组
        b[0]();//现在利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了;
        cin.get();
    }

仔细看上面的例子可能不用我多说大家也会知道是怎么一会事情了,最后我们做一个重点小结,只要记住这一点,对于理解利用函数指针构成数组进行函数间接调用就很容易了!
    void* a[]={t1,t2,t3};
    cout<<"比较t1()的内存地址和数组a[0]所存储的地址是否一致"<<t1<<"|"<<a[0]<<endl;

cout<<a[0]();//错误!指针数组是不能利用数组下标操作调用函数的

上面的这一小段中的错误行,为什么不能这么调用呢?

前一篇教程我们已经说的很清楚了,不过在这里我们还是复习一下概念,指针数组元素所保存的只是一个内存地址,既然只是个内存地址就
不可能进行a[0]()这样地址带括号的操作,而函数指针不同它是一个例外,函数指针只所以这么叫它就是因为它是指向函数指向内存的代码区的指针
,它被系统授予允许与()括号操作的权利,进行间接的函数调用,既然函数指针允许这么操作,那么被定义成函数指针的数组就一定是可以一样的操作的。

对上一例子改动:

    //a.c
    #include <iostream>
    #include <string>
    using namespace std;

void t1(){cout<<"test1\n";}
    void t2(){cout<<"test2\n";}
    void t3(){cout<<"test3\n";}

int main(int argc,char* argv[])
    {
            void* a[3];
            a[0]=(void *)t1;
            a[1]=(void *)t2;
            a[2]=(void *)t3;
            printf("t1=0x%x\n",*t1);
            cout<<"比较t1()的内存地址和数组a[0]所存储的地址是否一致"<<t1<<"|"<<a[0]<<endl;
    //      cout<<a[0]();//错误!指针数组是不能利用数组下标操作调用函数的
            typedef void (*fp)();//自定义一个函数指针类型
            fp b[]={t1,t2,t3}; //利用自定义类型fp把b[]定义趁一个指向函数的指针数组
            b[0]();//现在利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了;
            cin.get();
            return 0;
    }

编译:
    [root@CHN ]# g++ a.c -o a
    a.c: In function `int main(int, char**)':
    a.c:16: warning: the address of `void t1()', will always be `true'
    [root@CHN ]# ./a
    t1=0x804881c
    比较t1()的内存地址和数组a[0]所存储的地址是否一致1|0x804881c
    test1

c/c++函数指针(3)的更多相关文章

  1. C++虚函数和函数指针一起使用

    C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...

  2. 为什么 C++ 中成员函数指针是 16 字节?

    当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes a ...

  3. C++函数指针总结

    学习c++的过程中,指针是难点,熟悉了指针之后,还有一个让人很蛋疼的难点,那就是函数指针了.本博文详细介绍一下常见的各种坑爹的函数指针. 至于指针的详细学习,推荐这篇博文C++指针详解 与数据一样,函 ...

  4. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  5. 结构体内嵌函数指针实现C语言面向对象

    结构体内嵌函数指针 #include<stdio.h> void say(int age) { printf("我%d岁了\n",age); } struct stud ...

  6. 用typedef定义函数指针的问题

    在学习windows API的时候,遇到下面这段代码   以前见过的typedef的用法都是给一个数据类型取一个别名 typedef oldTypeName newTypeName   这种给数据类型 ...

  7. 你必须知道的指针基础-7.void指针与函数指针

    一.不能动的“地址”—void指针 1.1 void指针初探 void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据.和用int表示指针异曲同工,只是更明确是“指针” ...

  8. objective-c中的@selector()和 c /c++的函数指针

    先看tomcat里用到的代码: //然后开始动画 //把图片放到animationImages,接受数组参数 self.tom.animationImages = arrayImage; //设置时间 ...

  9. C++基础——函数指针 函数指针数组

    ==================================声明================================== 本文版权归作者所有. 本文原创,转载必须在正文中显要地注明 ...

  10. QT中使用函数指针

    想仿命令行,所以定义了一个类,让一个String 对应一个 function,将两者输入list容器. 类中定义了 QString commandStr; void (MainWindow::*com ...

随机推荐

  1. jenkins 执行远程linux命令

    在Jenkins中进行构建时,可能需要首先SSH登录到一个远程服务器以执行必要的脚本,然后再执行构建.这时,需要安装SSH Plugin,并进行如下配置.1.在Jenkins界面,系统管理->管 ...

  2. 如何在virtualenv虚拟环境中安装mysql-python

    接触过virtualenv后,想在这个虚拟环境中安装独立的开发环境.在安装MySQLdb时遇到错误 pc 09:09:30 File "/home/pc/work/VENV/py3/loca ...

  3. 为ubuntu添加多媒体以及flash等等常用包

    因为多媒体mp3,mp4以及flash等等都是一些有版权的东东,ubuntu不能直接集成到安装盘内,所以,做了2个常用包,来存放它们. ubuntu-restricted-addons ubuntu- ...

  4. 判断js对象是否拥有某一个属性的js代码

    js对象是否拥有某一个属性的判断方法有很多. 本文分享一个简单的方法,如下: <script> /** * 判断js对象是否具有某属性 * by www.jbxue.com */ var ...

  5. windows performance

    blogs.technet.com/b/perfguide/archive/2010/09/28/the-microsoft-pfe-performance-guide-start-here.aspx

  6. C# 获取FormData数据

    通常的方法是你创建一个 FormData 对象.然后你使用append方法来加入任何额外的key和他们的值.就像这样: var form = new FormData(); form.append(& ...

  7. C++面向对象程序设计的一些知识点(1)

    1.函数重载而不出错是有条件的: (1).这些函数位于同一域内,如同一类中.同一文件中. (2).这些同名函数具有不同的参数特征标(特征标是指形參的个数.类型.排列顺序等). 2.引用特性(与指针对比 ...

  8. navicat如何导入sql文件和导出sql文件

    http://jingyan.baidu.com/article/a65957f4976aad24e67f9b9b.html ************************************* ...

  9. Leetcode: LRU Cache 解题报告

    LRU Cache  Design and implement a data structure for Least Recently Used (LRU) cache. It should supp ...

  10. LeetCode: Populating Next Right Pointers in Each Node 解题报告

    Populating Next Right Pointers in Each Node TotalGiven a binary tree struct TreeLinkNode {      Tree ...