在阅读优秀的 c 语言开源程式的时候,我们经常会看到各种复杂的声明,顿时会让我们怀疑人生,怀疑自己是否真的看得懂 c 语言。然而冷静三秒钟,透过现象看本质,发现牛人写的代码并不是“天书”, 也是很好懂的,关键是要冷静和耐心去阅读。

从中的一个例子说起

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

下面我们来一步步分析:

我们知道变量的声明方式为:

1int a;

函数的声明方式为:

1int fn();

指针的声明方式为:

1int *a = 10;

函数指针的声明方式为:

1int (*fn)(); //fn 是一个指向返回int型的函数的指针

函数指针的调用方式:

 1typedef int (*fn_type)();
 2
 3int fn() {
 4    return 0;
 5}
 6
 7fn_type a = fn;
 8
 9printf("%d\n", a());
10//或者
11printf("%d\n", (*)a());

最简单的类型转换:

1void *p = 10;
2(int *)p;

在 c 语言中,去掉变量名,就是变量类型:

1int (*fn)();//fn 是一个指向返回int型的函数的指针
2int (*)(); //表示指向返回int型的函数的指针类型
3
4// 还可以用typedef
5
6typedef int (*fn_type)(); // fn_type 就是指向返回int型的函数指针类型

回到上面的声明,我们先分析里边的部分(void(*)())0,很显然void (*)()是一个指向返回 void 类型的函数的指针类型,可以简化为typedef void (*fn_ptr)(); (fn_ptr)0, 这样写是不是一下子明了了许多,原来是把0强制类型转换成了fn_ptr类型,也就是把0转成了一个指向返回void的函数的指针类型。这样的话,原式可以等价为((* fn_ptr )0)(), 实际上就是一个先转型,后调用的过程,即先把 0 转成函数指针,然后再调用函数。

再来看一个例子

1char *(*(*a[])())()

有了前面的基础,我相信再理解这个声明就没有那么困难了。

首先还是从内到外来解读:

1(*a[])()

很显然,在 c 语言中,去掉变量名就是变量类型,这里的 a 是一个指向函数的指针的数组,我们将其看做一个整体a0,那么再到外层为:

1char *(*a0)()

这样就很显然了,a0是一个指向返回char *类型的函数的指针。

那么综合起来解读这个声明即为:a 是一个指向 返回一个 指向返回char *类型的函数指针类型 的函数指针类型的数组。

哈哈,是不是读起来很绕口,很多时候确实是这样的,为了便于理解,也可参考下面的代码:

 1#include <stdio.h>
 2
 3typedef char * (*f1_ptr)();
 4typedef f1_ptr (*f2_ptr)();
 5
 6char *f1() {
 7    return "hello";
 8}
 9
10f1_ptr f2() {
11    return f1;
12}
13
14int main(int argc, char *argv[])
15{
16    char *(*(*a[1])())();
17    a[0] = f2;
18    printf("%s liubang\n", (a[0]())());
19    return 0;
20}

此外,我们还可以借助一些很好用的开源工具来帮我们解读这些声明,而且有时候,用英文表达这些声明能更好的帮我们解:

1liubang@venux:~$ sudo apt-get install cdecl -y
2liubang@venux:~$ cdecl
3Type `help' or `?' for help
4cdecl> explain char *(*(*a[])())()
5declare a as array of pointer to function returning pointer to function returning pointer to char
6cdecl> declare a as pointer to function returning struct tag
7struct tag (*a)()