原文地址: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. java 常见判断题

    1 根据下面的代码,String s = null;会抛出NullPointerException异常的有(). ) ) ) ) ) ) ) ) 说明:逻辑运算符:&&和|| 是按照“ ...

  2. unity 显示帧率

    Game视图右上角Stats按钮按下即可显示统计信息.

  3. MySql(七):MySQL性能调优——锁定机制与锁优化分析

    针对多线程的并发访问,任何一个数据库都有其锁定机制,它的优劣直接关系着数据的一致完整性与数据库系统的高并发处理性能.锁定机制也因此成了各种数据库的核心技术之一.不同数据库存储引擎的锁定机制是不同的,本 ...

  4. 国际化模块 angular-translate 简单方便快捷翻译中英文等多语言环境

    很多web服务面对的不仅仅是当地用户,多语言环境不仅能提升逼格,更重要是一种用户体验. angular.js 作为前后端拆分的解决方案之一,当然离不开前端框架处理国际化的问题,angular.js 官 ...

  5. hdu4901The Romantic Hero

    #include<iostream> #include<map> #include<string> #include<cstring> #include ...

  6. python2.7 跨文件全局变量的方法

    有关python实现跨文件全局变量的方法. 在使用Python编写的应用的过程中,有时会遇到多个文件之间传递同一个全局变量的情况.文件1:globalvar.py #!/usr/bin/env pyt ...

  7. informatica 参数文件配置

    Informatica 中 parameter file 参数文件配置规则: 参数文件的头部内容 [Global] All Integration Services, Integration Serv ...

  8. pgrep -f 和pkill -f

    pgrep -f abc 匹配出含abc的进程 并输出进程的pid pkill -f abc 杀掉含abc的所有进程

  9. ES6新特性概览1

    本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...

  10. WebForm中使用MVC

    http://www.cnblogs.com/encoding/articles/3556046.html ********************************************** ...