博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C 函数指针
阅读量:2345 次
发布时间:2019-05-10

本文共 5953 字,大约阅读时间需要 19 分钟。

 

1.函数指针的定义

  顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。看例子:

1
2
3
A) 
char  
* (*fun1)(
char  
* p1,
char  
* p2);
B) 
char  
* *fun2(
char  
* p1,
char  
* p2);
C) 
char  
* fun3(
char  
* p1,
char  
* p2);

 

形式1:返回类型(*函数名)(参数表) 

[cpp]  
 
  1. char (*pFun)(int);   
  2. char glFun(int a){ return;}   
  3. void main()   
  4. {   
  5.     pFun = glFun;   
  6.     (*pFun)(2);   
  7. }  


         第一行定义了一个指针变量pFun。首先我们根据前面提到的“形式1”认识到它是一个指向某种函数的指针,这种函数参数是一个int型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。
         第二行定义了一个函数glFun()。该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数——函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址
         然后就是main()函数了,它的第一句您应该看得懂了——它将函数glFun的地址赋值给变量pFun。main()函数的第二句中“*pFun”显然是取pFun所指向地址的内容,当然也就是取出了函数glFun()的内容,然后给定参数为2。
 

看看上面三个表达式分别是什么意思?

C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。
B) 也很简单,与C)表达式相比,唯一不同的就是函数的返回值类型为char**,是个二级指针。
A) fun1是函数名吗?回忆一下前面讲解数组指针时的情形。我们说数组指针这么定义或许更清晰:

1
int  
(*)[
10
] p;

再看看A)表达式与这里何其相似!明白了吧。这里fun1不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。同样,我们把这个表达式改写一下:

1
char  
* (*)(
char  
* p1,
char  
* p2) fun1;

这样子是不是好看一些呢?只可惜编译器不这么想。^_^。

 

使用typedef更直观更方便

形式1:typedef  返回类型(*新类型)(参数表)

[cpp]  
 
  1. typedef char (*PTRFUN)(int);   
  2. PTRFUN pFun;   
  3. char glFun(int a){ return;}   
  4. void main()   
  5. {   
  6.     pFun = glFun;   
  7.     (*pFun)(2);   
  8. }   

            typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。
           第二行的代码便使用这个新类型定义了变量pFun,此时就可以像使用形式1一样使用这个变量了。

[cpp]  
 
  1. #include <stdio.h>  
  2. #include <assert.h>  
  3.   
  4. typedef int (*FP_CALC)(int,int);//定义一个函数指针类型  
  5.   
  6. int add(int a, int b)  
  7. {  
  8.     return a + b;  
  9. }  
  10.   
  11. int sub(int a, int b)  
  12. {  
  13.     return a - b;  
  14. }  
  15.   
  16. int mul(int a, int b)  
  17. {  
  18.     return a * b;  
  19. }  
  20.   
  21. int div(int a, int b)  
  22. {  
  23.     return b ? a/b : -1;  
  24. }  
  25.   
  26. //定义一个函数,参数为op,返回一个指针,该指针类型为拥有两个int参数、  
  27. //返回类型为int的函数指针。它的作用是根据操作符返回相应函数的地址  
  28. FP_CALC calc_func(char op)  
  29. {  
  30.     switch( op )  
  31.     {  
  32.     case '+':  
  33.         return add;  
  34.     case '-':  
  35.         return sub;  
  36.     case '*':  
  37.         return mul;  
  38.     case '/':  
  39.         return div;  
  40.     default:  
  41.         return NULL;  
  42.     }  
  43.     return NULL;  
  44. }  
  45.   
  46. //s_calc_func为函数,它的参数是 op,     
  47. //返回值为一个拥有两个int参数、返回类型为int的函数指针    
  48. int (*s_calc_func(char op)) (int , int)  
  49. {  
  50.     return calc_func(op);  
  51. }  
  52.   
  53. //最终用户直接调用的函数,该函数接收两个int整数,  
  54. //和一个算术运算符,返回两数的运算结果  
  55. int calc(int a, int b, char op)  
  56. {  
  57.     FP_CALC fp = calc_func(op);  
  58.     int (*s_fp)(int,int) = s_calc_func(op);//用于测试  
  59.   
  60.     assert(fp == s_fp);// 可以断言这两个是相等的  
  61.   
  62.     if(fp)  
  63.         return fp(a,b);  
  64.     else  
  65.         return -1;  
  66. }  
  67.   
  68. void main()  
  69. {  
  70.     int a = 100, b = 20;  
  71.   
  72.     printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));  
  73.     printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));     
  74.     printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));     
  75.     printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));   
  76. }  



2.函数指针使用的例子

  上面我们定义了一个函数指针,但如何来使用它呢?先看如下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>
 
char  
* fun(
char  
* p1,
char  
* p2)
{
  
int  
i = 
0
;
  i = strcmp(p1,p2);
 
  
if  
(
0  
== i)
  {
    
return  
p1;
  }
  
else
  {
    
return  
p2;
  }
}
 
int  
main()
{
  
char  
* (*pf)(
char  
* p1,
char  
* p2);
  pf = &fun;
  (*pf) (
"aa"
,
"bb"
);
  
return  
0
;
}

  我们使用指针的时候,需要通过钥匙(“*”)来取其指向的内存里面的值,函数指针使用也如此。通过用(*pf)取出存在这个地址上的函数,然后调用它。

  这里需要注意到是,在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。这个例子很简单,就不再详细讨论了。

 

3.*(int*)&p ----这是什么?

  也许上面的例子过于简单,我们看看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
void  
Function()
{
  printf(
"Call Function!\n"
);
}<br>
int  
main()
{
  
void  
(*p)();
  *(
int
*)&p=(
int
)Function;
  (*p)();
  
return  
0
;
} 

这是在干什么?*(int*)&p=(int)Function;表示什么意思?
别急,先看这行代码:

1
void  
(*p)();

这行代码定义了一个指针变量p,p指向一个函数,这个函数的参数和返回值都是void。

&p是求指针变量p本身的地址,这是一个32位的二进制常数(32位系统)。
(int*)&p表示将地址强制转换成指向int类型数据的指针
(int)Function表示将函数的入口地址强制转换成int类型的数据。
分析到这里,相信你已经明白*(int*)&p=(int)Function;表示将函数的入口地址赋值给指针变量p。

那么(*p) ();就是表示对函数的调用。

讲解到这里,相信你已经明白了。其实函数指针与普通指针没什么差别,只是指向的内容不同而已。
使用函数指针的好处在于,可以将实现同一功能的多个模块统一起来标识,这样一来更容易后期的维护,系统结构更加清晰。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。

 

 

4.(*(void(*) ())0)()------这是什么?

  是不是感觉上面的例子太简单,不够刺激?好,那就来点刺激的,看下面这个例子:

1
(*(
void
(*) ())
0
)();

这是《C Traps and Pitfalls》这本经典的书中的一个例子。没有发狂吧?下面我们就来分析分析:

1
2
3
4
第一步:
void
(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(
void
(*) ())
0
,这是将
0
强制转换为函数指针类型,
0
是一个地址,也就是说一个函数存在首地址为
0
的一段区域内。
第三步:(*(
void
(*) ())
0
),这是取
0
地址开始的一段内存里面的内容,其内容就是保存在首地址为
0
的一段区域内的函数。
第四步:(*(
void
(*) ())
0
)(),这是函数调用。

好像还是很简单是吧,上面的例子再改写改写:

1
(*(
char
**(*) (
char  
**,
char  
**))
0
) ( 
char  
**,
char  
**);

如果没有上面的分析,肯怕不容易把这个表达式看明白吧。不过现在应该是很简单的一件事了。读者以为呢?

 

5.函数指针数组

 

  现在我们清楚表达式 

1
char  
* (*pf)(
char  
* p);

定义的是一个函数指针pf。既然pf是一个指针,那就可以储存在一个数组里。把上式修改一下:

1
char  
* (*pf[
3
])(
char  
* p);

这是定义一个函数指针数组

 

  它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

  这念起来似乎有点拗口。不过不要紧,关键是你明白这是一个指针数组,是数组。函数指针数组怎么使用呢?这里也给出一个非常简单的例子,只要真正掌握了使用方法,再复杂的问题都可以应对。

 

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <string.h>
<br>
char  
* fun1(
char  
* p)
{
  printf(
"%s\n"
,p);
  
return  
p;
}
 
char  
* fun2(
char  
* p)
{
  printf(
"%s\n"
,p);
  
return  
p;
}
char  
* fun3(
char  
* p)
{
  printf(
"%s\n"
,p);
  
return  
p;
}
<br>
int  
main()
{
  
char  
* (*pf[
3
])(
char  
* p);
  pf[
0
] = fun1; 
//可以直接用函数名
  pf[
1
] = &fun2; 
//可以用函数名加上取地址符
  pf[
2
] = &fun3;<br>
  pf[
0
](
"fun1"
);
  pf[
0
](
"fun2"
);
  pf[
0
](
"fun3"
);
  
return  
0
;
} 

 

6.函数指针数组的指针

  看着这个标题没发狂吧?函数指针就够一般初学者折腾了,函数指针数组就更加麻烦,现在的函数指针数组指针就更难理解了。
其实,没这么复杂。前面详细讨论过数组指针的问题,这里的函数指针数组指针不就是一个指针嘛。只不过这个指针指向一个数组,这个数组里面存的都是指向函数的指针。仅此而已。

下面就定义一个简单的函数指针数组指针:

1
char  
* (*(*pf)[
3
])(
char  
* p);

注意,这里的pf和上一节的pf就完全是两码事了。上一节的pf并非指针,而是一个数组名;这里的pf确实是实实在在的指针。这个指针指向一个包含了3个元素的数组;这个数字里面存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

  这比上一节的函数指针数组更拗口。其实你不用管这么多,明白这是一个指针就ok了。其用法与前面讲的数组指针没有差别。下面列一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>
 
char  
* fun1(
char  
* p)
{
    
printf(
"%s\n"
,p);
    
return  
p;
}
 
char  
* fun2(
char  
* p)
{
    
printf(
"%s\n"
,p);
    
return  
p;
}
 
char  
* fun3(
char  
* p)
{
    
printf(
"%s\n"
,p);
    
return  
p;
}
 
int  
main()
{
    
char  
* (*a[
3
])(
char  
* p);
    
char  
* (*(*pf)[
3
])(
char  
* p);
    
pf = &a;
 
    
a[
0
] = fun1;
    
a[
1
] = &fun2;
    
a[
2
] = &fun3;
 
    
pf[
0
][
0
](
"fun1"
);
    
pf[
0
][
1
](
"fun2"
);
    
pf[
0
][
2
](
"fun3"
);
    
return  
0
;
}

  

转载地址:http://eqnvb.baihongyu.com/

你可能感兴趣的文章
Codility经典算法题之九:MissingInteger
查看>>
静态导入
查看>>
java 获取路径
查看>>
spring boot 打印sql
查看>>
我的死锁经历
查看>>
spring boot日志配置
查看>>
list排序
查看>>
搭建zookeeper集群
查看>>
1005. 数独
查看>>
1006. 求和游戏
查看>>
IDEA eclipse 控制台日志输出到文件
查看>>
1022. Fib数列
查看>>
一些开源项目
查看>>
将博客搬至CSDN
查看>>
MySQL 中事务的实现
查看>>
CheckStyle
查看>>
IDE配置jvm参数
查看>>
内存溢出
查看>>
Spring Cloud 声明式服务调用 Feign
查看>>
JVM实用参数(一)JVM类型以及编译器模式
查看>>