原文地址: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. PHP Client for Mysql Binlog

    PHP解析MySQL Binlog,依赖于mysql-replication-listener库 详见:https://github.com/bullsoft/php-binlog Install M ...

  2. MongoDB创建索引(不锁库方法)

    db.collection.createIndex( { a: 1 }, { background: true } )https://docs.mongodb.org/manual/tutorial/ ...

  3. 关于WCF服务的调试跟踪

    关于WCF服务的调试跟踪信息,请利用好以下几个工具,具体的例子MSDN上都有,进去看吧... 服务跟踪查看器工具 (SvcTraceViewer.exe): http://msdn.microsoft ...

  4. Meta Blogging

    Meta Blogging 由来 偶然想到说如果哪一天cnblogs挂了,那之前记录的随笔怎么办?可不可以把它们给download下来本地保存一份.正好看到有个库叫requests, 干嘛不试试看呢. ...

  5. Oracle PLSQL Demo - 27.Declare & Run Sample

    declare v_sal ) :; begin --if you could not see the output in console, you should set output on firs ...

  6. Along with all the above benefits, you cannot overlook the space efficiency and performance gains in using DataFrames and Dataset APIs for two reasons.

    Of all the developers’ delight, a set of APIs that makes them productive, that are easy to use, and ...

  7. 转-webstorm快捷键

    默认配置-Eclipse的常用快捷键对照表 查找/代替 Webstorm快捷键 Eclipse快捷键 说明 ctrl+shift+N ctrl+shift+R 通过文件名快速查找工程内的文件(必记) ...

  8. C++编程思想

    写在前面的话:自己看书的时候不是太习惯边看边做上面的例子,因为感觉那样子看书太慢了,但是这样子又会因为看的太快,之后什么都记不住,所以就想写写读书笔记 第六章

  9. hadoop集群搭建namenode无法启动问题

    一定要确保机器名没有下划线和.等特殊字符 搞了好久,终于找到了是上面这个原因. 搭建好了,下一步就是调优喽!

  10. 在ajax post处理文件下载

    我有一个JavaScript应用程序需要使用ajax post请求发送到某个URL,然后后端会根据请求中的参数进行相应的工作,生成一个可下载的压缩包,等待下载.必须使用的ajax的原因是这里需要模拟提 ...