搞懂C语言里面的函数指针
本文讲解C语言里面的函数指针的类型声明方式。这篇文章按照难度分几层撰写,大家量力而行进行阅读。
基础
C语言里面的函数指针定义由三部分组成:
(函数的返回类型)(*指针的变量名)(函数的参数类型)
比如我们有这样的代码:
char *x(char *p) {
return p;
}
那么指向它的指针该怎样定义呢?
根据上面的原则,应该是:
char *(*px)(char *)
因为x函数的返回值是char *
,参数是char *p
,因此参数类型是char *
。所以上面的px
指针就声明如上,并使用px
指针对x
函数进行调用:
px = &x;
px("Hello");
我们可以写代码验证:
#include <stdio.h>
char *x(char *p) {
return p;
}
int main() {
char *(*px)(char *);
px = &x;
printf("%s\n", px("Hello"));
}
程序输出如下:
Hello
也可以这样写:
px = x;
上面这个和下面的赋值语句是等价的:
px = &x;
如果你觉得上面px
的声明看着难受,或者说,我们想定义多个函数指针变量的时候,就可以先用typedef
定义好类型声明:
typedef char *(*px_t)(char *);
然后使用定义的这个px_x
类型来声明函数的指针变量:
px_t px = x;
px_t px2 = x;
然后使用即可。下面是完整的代码:
#include <stdio.h>
char* x(char *p) {
return p;
}
int main() {
typedef char* (*px_t)(char*);
px_t px = x;
px_t px2 = x;
printf("%s\n", px("Hello"));
printf("%s\n", px2("Hello"));
}
程序输出如下:
Hello
Hello
下面继续讲解更抽象一层的声明。
进阶
接下来我们做一些更复杂的东西。假设我们有两个类型一致的函数:
char *x(char *p) {
return p;
}
char *y(char *p) {
return p;
}
我们想用一个函数的指针数组指向它们两个,也是可以的。声明同样遵守一开始的那个三段法则:
char *(*p_xy[2])(char *);
这样,我们声明了一个函数指针的数组,这个数组包含两个函数的指针变量。然后我们让两个指针各指向x
函数和y
函数:
p_xy[0] = x;
p_xy[1] = y;
然后调用:
printf("%s\n", p_xy[0]("Banana"));
printf("%s\n", p_xy[1]("Apple"));
代码输出如下:
Banana
Apple
我们同样可以使用typedef来定义类型:
typedef char *(*p_xy_t[2])(char *);
p_xy_t p_xy;
p_xy[0] = x;
p_xy[1] = y;
和上面的定义方式是等价的。
困难
接下来,我们再抽象一层,做一个指针,指向p_xy
。这里别晕:p_xy
是函数指针变量的数组,然后我们要做一个p_to_p_xy
指针,指向p_xy
。
声明如下:
char *(*(*p_to_p_xy))(char *);
仔细观察,上面声明的p_to_p_xy
就是指向p_xy
的指针。我们使用它指向p_xy
的地址:
p_to_p_xy = &p_xy;
调用方法如下:
printf("%s\n", p_to_p_xy[0]("Earth"));
printf("%s\n", p_to_p_xy[1]("Mars"));
同样地,上面的类型定义也可以用typedef
来做:
typedef char *(*(*p_to_p_xy_t))(char *);
然后使用这个类型定义来声明变量:
p_to_p_xy_t p_to_p_xy;
这样对于这个类型的使用者来讲,就简单多了。
此外,如果像之前那样,把p_xy_t
用typedef
定义好的话,那么上面这个定义可以省掉,我们可以这样定义p_to_p_xy
:
p_xy_t *p_to_p_xy;
这个很好理解,我们定义了一个指向p_xy_t
类型的指针。这就是使用typedef
的好处。
然后我们就可以让p_to_p_xy
指向p_xy
的地址:
p_to_p_xy = &p_xy;
下面是调用方法:
printf("%s\n", (*p_to_p_xy)[0]("Earth"));
printf("%s\n", (*p_to_p_xy)[1]("Mars"));
以上是抽象一层,下面我们继续再封装一层:
放弃
最后,为了展示这个逻辑,我们再抽象一层,来做一个保存p_to_p_xy
这样类型的指针的数组。
首先,我们做两个p_to_p_xy
这样类型的变量:
p_xy_t *p_to_p_xy;
p_xy_t *p2_to_p_xy;
上面两个假设全都各自指向x
和y
函数的指针数组:
p_to_p_xy = &p_xy;
p2_to_p_xy = &p_xy;
然后我们要做指针数组指向它们两个。
为了展示最底层逻辑,我们不用typedef
,从最基础类型进行声明:
char *(*(*p_to_p_to_p_xy[2]))(char *);
然后赋值:
p_to_p_to_p_xy[0] = p_to_p_xy;
p_to_p_to_p_xy[1] = p2_to_p_xy;
然后使用:
printf("%s\n", p_to_p_to_p_xy[0][0]("Are"));
printf("%s\n", p_to_p_to_p_xy[0][1]("you"));
printf("%s\n", p_to_p_to_p_xy[1][0]("ok"));
printf("%s\n", p_to_p_to_p_xy[1][1]("?"));
输出如下:
Are
you
ok
?
以上就是对类型声明的一个展示。同样地,给出使用typedef
简化后的版本。
骨灰
最后,我们把各种typedef
或不使用typedef
的声明方式都列出来,供大家参考:
#include <stdio.h>
#include <stdlib.h>
char *x(char *p) {
return p;
}
char *y(char *p) {
return p;
}
int main() {
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
char *(*p_x)(char *);
char *(*p2_x)(char *);
p_x = x;
p2_x = &x;
printf("%s\n", p_x("Red"));
printf("%s\n", p2_x("Blue"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
typedef char *(*p_x_t)(char *);
p_x_t p3_x = x;
printf("%s\n", p3_x("Black"));
printf("%s\n", p3_x("White"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
char *(*p_xy[2])(char *);
p_xy[0] = x;
p_xy[1] = y;
printf("%s\n", p_xy[0]("Tomato"));
printf("%s\n", p_xy[1]("Kiwi"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
typedef char *(*p_xy_t[2])(char *);
p_xy_t p2_xy;
p2_xy[0] = x;
p2_xy[1] = y;
printf("%s\n", p2_xy[0]("Banana"));
printf("%s\n", p2_xy[1]("Apple"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
p_xy_t *pp_xy;
pp_xy = &p_xy;
printf("%s\n", (*pp_xy)[0]("Earth"));
printf("%s\n", (*pp_xy)[1]("Mars"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
char *(*(*pp2_xy))(char *);
pp2_xy = &p_xy;
printf("%s\n", pp2_xy[0]("Jupiter"));
printf("%s\n", pp2_xy[1]("Sun"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
typedef char *(*(*pp_xy_t))(char *);
pp_xy_t pp3_xy = &p_xy;
printf("%s\n", pp3_xy[0]("Mercury"));
printf("%s\n", pp3_xy[1]("Neptune"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
char *(*(*pPp_xy[2]))(char *);
pPp_xy[0] = pp_xy;
pPp_xy[1] = pp2_xy;
printf("%s\n", pPp_xy[0][0]("Are"));
printf("%s\n", pPp_xy[0][1]("you"));
printf("%s\n", pPp_xy[1][0]("ok"));
printf("%s\n", pPp_xy[1][1]("?"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
pp_xy_t pPp2_xy[2];
pPp2_xy[0] = pp_xy;
pPp2_xy[1] = pp2_xy;
printf("%s\n", pPp2_xy[0][0]("I"));
printf("%s\n", pPp2_xy[0][1]("am"));
printf("%s\n", pPp2_xy[1][0]("fine"));
printf("%s\n", pPp2_xy[1][1]("!"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
p_xy_t *pPp3_xy[2];
pPp3_xy[0] = pp_xy;
pPp3_xy[1] = pp2_xy;
printf("%s\n", (*pPp3_xy[0])[0]("How"));
printf("%s\n", (*pPp3_xy[0])[1]("are"));
printf("%s\n", (*pPp3_xy[1])[0]("you"));
printf("%s\n", (*pPp3_xy[1])[1]("?"));
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
以上就是函数指针的一些声明方式,希望对大家有所帮助。