一 通常的函数调用
    一个通常的函数调用的例子:
//自行包含头文件

  1. void MyFun(int x); //此处的申明也可写成:void MyFun( int );
  2. int main(int argc, char* argv[])
  3. {
  4. MyFun(10); //这里是调用MyFun(10);函数
  5. return 0;
  6. }
  7. void MyFun(int x) //这里定义一个MyFun函数
  8. {
  9. printf(“%d\n”,x);
  10. }
    这个MyFun函数是一个无返回值的函数,它并不完成什么事情。这种调用函数的格式你应该是很熟悉的吧!看主函数中调用MyFun函数的书写格式:
MyFun(10);
    我们一开始只是从功能上或者说从数学意义上理解MyFun这个函数,知道MyFun函数名代表的是一个功能(或是说一段代码)。
    直到——
    学习到函数指针概念时。我才不得不在思考:函数名到底又是什么东西呢?
    (不要以为这是没有什么意义的事噢!呵呵,继续往下看你就知道了。)

二 函数指针变量的申明
    就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的首地址也以存储在某个函数指针变量里的。这样,我就可以通过这个函数指针变量来调用所指向的函数了。
    在C系列语言中,任何一个变量,总是要先申明,之后才能使用的。那么,函数指针变量也应该要先申明吧?那又是如何来申明呢?以上面的例子为例,我来申明一个可以指向MyFun函数的函数指针变量FunP。下面就是申明FunP变量的方法:
void (*FunP)(int) ;   //也可写成void (*FunP)(int x);
    你看,整个函数指针变量的申明格式如同函数MyFun的申明处一样,只不过——我们把MyFun改成(*FunP)而已,这样就有了一个能指向MyFun函数的指针FunP了。(当然,这个FunP指针变量也可以指向所有其它具有相同参数及返回值的函数了。)

三 通过函数指针变量调用函数
    有了FunP指针变量后,我们就可以对它赋值指向MyFun,然后通过FunP来调用MyFun函数了。看我如何通过FunP指针变量来调用MyFun函数的:
//自行包含头文件

  1. void MyFun(int x); //这个申明也可写成:void MyFun( int );
  2. void (*FunP)(int ); //也可申明成void(*FunP)(int x),但习惯上一般不这样。
  3. int main(int argc, char* argv[])
  4. {
  5. MyFun(10); //这是直接调用MyFun函数
  6. FunP=&MyFun; //将MyFun函数的地址赋给FunP变量
  7. (*FunP)(20); //这是通过函数指针变量FunP来调用MyFun函数的。
  8. }
  9. void MyFun(int x) //这里定义一个MyFun函数
  10. {
  11. printf(“%d\n”,x);
  12. }
    请看黑体字部分的代码及注释。
    运行看看。嗯,不错,程序运行得很好。
    哦,我的感觉是:MyFun与FunP的类型关系类似于int 与int *的关系。函数MyFun好像是一个如int的变量(或常量),而FunP则像一个如int *一样的指针变量。
int i,*pi;
pi=&i;    //与FunP=&MyFun比较。
    (你的感觉呢?)
    呵呵,其实不然——

四 调用函数的其它书写格式
函数指针也可如下使用,来完成同样的事情:
//自行包含头文件

  1. void MyFun(int x);
  2. void (*FunP)(int ); //申明一个用以指向同样参数,返回值函数的指针变量。
  3. int main(int argc, char* argv[])
  4. {
  5. MyFun(10); //这里是调用MyFun(10);函数
  6. FunP=MyFun; //将MyFun函数的地址赋给FunP变量
  7. FunP(20); //这是通过函数指针变量来调用MyFun函数的。
  8. return 0;
  9. }
  10. void MyFun(int x) //这里定义一个MyFun函数
  11. {
  12. printf(“%d\n”,x);
  13. }
我改了黑体字部分(请自行与之前的代码比较一下)。
运行试试,啊!一样地成功。
咦?
FunP=MyFun;
可以这样将MyFun值同赋值给FunP,难道MyFun与FunP是同一数据类型(即如同的int 与int的关系),而不是如同int 与int*的关系了?(有没有一点点的糊涂了?)
看来与之前的代码有点矛盾了,是吧!所以我说嘛!
请容许我暂不给你解释,继续看以下几种情况(这些可都是可以正确运行的代码哟!):
代码之三:

  1. int main(int argc, char* argv[])
  2. {
  3. MyFun(10); //这里是调用MyFun(10);函数
  4. FunP=&MyFun; //将MyFun函数的地址赋给FunP变量
  5. FunP(20); //这是通过函数指针变量来调用MyFun函数的。
  6. return 0;
  7. }
  8. 代码之四:
  9. int main(int argc, char* argv[])
  10. {
  11. MyFun(10); //这里是调用MyFun(10);函数
  12. FunP=MyFun; //将MyFun函数的地址赋给FunP变量
  13. (*FunP)(20); //这是通过函数指针变量来调用MyFun函数的。
  14. return 0;
  15. }

真的是可以这样的噢!
(哇!真是要晕倒了!)
还有呐!看——

  1. int main(int argc, char* argv[])
  2. {
  3. (*MyFun)(10); //看,函数名MyFun也可以有这样的调用格式
  4. return 0;
  5. }

你也许第一次见到吧:函数名调用也可以是这样写的啊!(只不过我们平常没有这样书写罢了。)
那么,这些又说明了什么呢?
呵呵!假使我是“福尔摩斯”,依据以往的知识和经验来推理本篇的“新发现”,必定会由此分析并推断出以下的结论:
1. 其实,MyFun的函数名与FunP函数指针都是一样的,即都是函数指针。MyFun函数名是一个函数指针常量,而FunP是一个函数数指针变量,这是它们的关系。
2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许MyFun(10);这种形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3. 为统一起见,FunP函数指针变量也可以FunP(10)的形式来调用。
4. 赋值时,即可FunP=&MyFun形式,也可FunP=MyFun。
上述代码的写法,随便你爱怎么着!
请这样理解吧!这可是有助于你对函数指针的应用喽!
最后——
补充说明一点:在函数的申明处:
void MyFun(int );    //不能写成void (*MyFun)(int )。
void (*FunP)(int );   //不能写成void FunP(int )。
(请看注释)这一点是要注意的。

五 定义某一函数的指针类型:
就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数指针变量。
我先给你一个自定义数据类型的例子。

  1. typedef int* PINT; //为int* 类型定义了一个PINT的别名
  2. int main()
  3. {
  4. int x;
  5. PINT px=&x; //与int * px=&x;是等价的。PINT类型其实就是int * 类型
  6. *px=10; //px就是int*类型的变量
  7. return 0;
  8. }

根据注释,应该不难看懂吧!(虽然你可能很少这样定义使用,但以后学习Win32编程时会经常见到的。)
下面我们来看一下函数指针类型的定义及使用:(请与上对照!)
//自行包含头文件

  1. void MyFun(int x); //此处的申明也可写成:void MyFun( int );
  2. typedef void (*FunType)(int ); //这样只是定义一个函数指针类型
  3. FunType FunP; //然后用FunType类型来申明全局FunP变量
  4. int main(int argc, char* argv[])
  5. {
  6. //FunType FunP; //函数指针变量当然也是可以是局部的 ,那就请在这里申明了。
  7. MyFun(10);
  8. FunP=&MyFun;
  9. (*FunP)(20);
  10. return 0;
  11. }
  12. void MyFun(int x)
  13. {
  14. printf(“%d\n”,x);
  15. }

看黑体部分:
首先,在void (*FunType)(int ); 前加了一个typedef 。这样只是定义一个名为FunType函数指针类型,而不是一个FunType变量。
然后,FunType FunP;  这句就如PINT px;一样地申明一个FunP变量。
其它相同。整个程序完成了相同的事。
这样做法的好处是:
有了FunType类型后,我们就可以同样地、很方便地用FunType类型来申明多个同类型的函数指针变量了。如下:
FunType FunP2;
FunType FunP3;
//……

六 函数指针作为某个函数的参数
既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。所以,你还应知道函数指针是如何作为某个函数的参数来传递使用的。
给你一个实例:
要求:我要设计一个CallMyFun函数,这个函数可以通过参数中的函数指针值不同来分别调用MyFun1、MyFun2、MyFun3这三个函数(注:这三个函数的定义格式应相同)。
实现:代码如下:
//自行包含头文件

  1. void MyFun1(int x);
  2. void MyFun2(int x);
  3. void MyFun3(int x);
  4. typedef void (*FunType)(int ); //②. 定义一个函数指针类型FunType,与①函数类型一至
  5. void CallMyFun(FunType fp,int x);
  6. int main(int argc, char* argv[])
  7. {
  8. CallMyFun(MyFun1,10); //⑤. 通过CallMyFun函数分别调用三个不同的函数
  9. CallMyFun(MyFun2,20);
  10. CallMyFun(MyFun3,30);
  11. }
  12. void CallMyFun(FunType fp,int x) //③. 参数fp的类型是FunType。
  13. {
  14. fp(x);//④. 通过fp的指针执行传递进来的函数,注意fp所指的函数是有一个参数的
  15. }
  16. void MyFun1(int x) // ①. 这是个有一个参数的函数,以下两个函数也相同
  17. {
  18. printf(“函数MyFun1中输出:%d\n”,x);
  19. }
  20. void MyFun2(int x)
  21. {
  22. printf(“函数MyFun2中输出:%d\n”,x);
  23. }
  24. void MyFun3(int x)
  25. {
  26. printf(“函数MyFun3中输出:%d\n”,x);
  27. }

输出结果:略

分析:(看我写的注释。你可按我注释的①②③④⑤顺序自行分析。)

 
以上部分为转载网友所述。原文地址为:http://blog.pfan.cn/whyhappy/6030.html
 
七 地址跳转
void(*reset)(void)= (void(*)(void))0。

void(*reset)(void)就是函数指针定义,(void(*)(void))0是强制类型转换操作,将数值“0”强制转换为函数指针地址“0”。

通过调用reset()函数,程序就会跳转到程序执行的“0”地址处重新执行。在一些其他高级单片机Bootloader中,如NBoot、UBoot、EBoot,经常通过这些Bootloader进行下载程序,然后通过函数指针跳转到要执行程序的地址处。

1   void (*theUboot)(void);
    。。。。
    theUboot = (void (*)(void))(0x30700000);
    theUboot();
    。。。。。

2   (*(void (*)(void))(0x30700000))();

强制类型转换,将一个绝对地址转换为一个函数指针,并调用这个函数以跳转到前面提到的绝对地址.
翻译成汇编就是:
mov r0,0x30700000;
mov pc,r0

对于(*(void (*)(void))(0x30700000))();
可以这样理解

首先(void( * )(void) )是一个强制类型转换符,他将后面的0x30700000这个无符号整数强制转化为一个函数指针,该函数指针所指向的函数入口参数为 void,返回值也是void 。 如果到这步你看懂了,那么设(void (*)(void))(0x30700000)为 fp; 那么上面的表达式就可以简化为 (*fp)();   OK,这下就清楚了吧,我们将上面转化好的函数指针进行引用(也就是调用函数指针指向的函数)。

from:http://blog.chinaunix.net/space.php?uid=25524263&do=blog&id=2888273

C 函数指针详解的更多相关文章

  1. C语言函数名与函数指针详解

    一.通常的函数调用 一个通常的函数调用的例子: /* 自行包含头文件 */ void MyFun(int x); /* 此处的声明也可写成:void MyFun(int) */ int main(in ...

  2. C/C++函数指针详解(转)

    这两天在研究C/C++的函数指针,找到一篇讲解比较详细的博客,内容有点多,但是讲解得比较详细,适合初学者.特转之: 1.     无处不见的函数指针 使用函数指针可以设计出更优雅的程序,比如设计一个集 ...

  3. 7--OC中NSLog函数输出格式详解

    OC中NSLog函数输出格式详解 • %@ 对象 • %d, %i 整数 • %u 无符整形 • %f 浮点/双字 • %x, %X 二进制整数 • %o 八进制整数 • %zu size_t • % ...

  4. 【C++】智能指针详解(一):智能指针的引入

    智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...

  5. Go 延迟函数 defer 详解

    Go 延迟函数 defer 详解 Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在实际应用中,很多 gopher 并没有真正搞明白 defer.re ...

  6. php中的PDO函数库详解

    PHP中的PDO函数库详解 PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,P ...

  7. C语言对文件的操作函数用法详解2

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const  ...

  8. C语言对文件的操作函数用法详解1

    在ANSIC中,对文件的操作分为两种方式,即: 流式文件操作 I/O文件操作 一.流式文件操作 这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下: typedef str ...

  9. c/c++指针详解(一)

    一:相关概念 1.指针数组:int *p[6]               是数组,是一个存放指针的数组,也就是里面存放的是地址. 2.数组指针:int (*p)[6]                 ...

随机推荐

  1. 【leetcode】328. Odd Even Linked List

    题目如下: Given a singly linked list, group all odd nodes together followed by the even nodes. Please no ...

  2. 安卓Unity3d游戏的逆向破解

    使用到的工具 ApkToolBox .NET Reflector Reflexil(.NET程序编辑器) 怎么判定它是Unity游戏呢 一般的路径格式是这样的assets\bin\Data\Manag ...

  3. JVM调优学习 【更新中】

    JVM调优(jdk1.8) 老生常谈,面试吹牛的的最佳谈资,在接下来的几天里,我找了点资料来对其进行一波学习: 本地环境是不需要对我们的虚拟机进行优化的,一般在生产环境下,也就是Linux下才有对JV ...

  4. bzoj1098题解

    [题意分析] 给你一张无向图,求其补图的联通块数及各个联通块大小. [解题思路] 暴搜! 然而n2会T怎么办? 仔细观察发现m远小于n2,也就是说这是一张极其稠密的补图. 这时就要用到黑科技了:flo ...

  5. Arduino与无源蜂鸣器

    1.Arduino无源蜂鸣器传感器模块 我们可以使用Arduino进行很多互动作品,其中最常用的是声光显示器.之前的所有实验都与LED有关.但是,这个实验中的电路可以产生声音.通常,实验是通过蜂鸣器或 ...

  6. swapper_pg_dir的作用

    在内存系统初始化过程中,有如下代码: 1: static void __init pagetable_init(void) 2: { 3: pgd_t *pgd_base = swapper_pg_d ...

  7. Codeforces 1191B Tokitsukaze and Mahjong

    题目链接:http://codeforces.com/problemset/problem/1191/B 题意:类似于麻将,三个一样花色一样数字的,或者三个同花顺就赢了,新抽的能当任何类型,问至少几个 ...

  8. upc组队赛2 Master of GCD 【线段树区间更新 || 差分】

    Master of GCD 题目描述 Hakase has n numbers in a line. At fi rst, they are all equal to 1. Besides, Haka ...

  9. Jenkins和Docker

    由于采用了Docker版的Jenkins,导致在Jenkins里无法调用Docker命令行工具进行Docker镜像构建 有三种解决方案: 1. 安装Docker插件,利用Jenkins插件进行构建 2 ...

  10. docker 详细安装及问题排查

    一.安装docker 1.Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker . 通过 uname -r 命令查看 ...