在阅读优秀的 c 语言开源程式的时候,我们经常会看到各种复杂的声明,顿时会让我们怀疑人生,怀疑自己是否真的看得懂 c 语言。然而冷静三秒钟,透过现象看本质,发现牛人写的代码并不是“天书”, 也是很好懂的,关键是要冷静和耐心去阅读。
从“C Traps and Pitfalls”中的一个例子说起
(*(void(*)())0)()
下面我们来一步步分析:
我们知道变量的声明方式为:
int a;
函数的声明方式为:
int fn();
指针的声明方式为:
int *a = 10;
函数指针的声明方式为:
int (*fn)(); //fn 是一个指向返回int型的函数的指针
函数指针的调用方式:
typedef int (*fn_type)();
int fn() {
return 0;
}
fn_type a = fn;
printf("%d\n", a());
//或者
printf("%d\n", (*)a());
最简单的类型转换:
void *p = 10;
(int *)p;
在 c 语言中,去掉变量名,就是变量类型:
int (*fn)();//fn 是一个指向返回int型的函数的指针
int (*)(); //表示指向返回int型的函数的指针类型
// 还可以用typedef
typedef 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 转成函数指针,然后再调用函数。
再来看一个例子
char *(*(*a[])())()
有了前面的基础,我相信再理解这个声明就没有那么困难了。
首先还是从内到外来解读:
(*a[])()
很显然,在 c 语言中,去掉变量名就是变量类型,这里的 a 是一个指向函数的指针的数组,我们将其看做一个整体a0
,那么再到外层为:
char *(*a0)()
这样就很显然了,a0
是一个指向返回char *
类型的函数的指针。
那么综合起来解读这个声明即为:a 是一个指向 返回一个 指向返回char *
类型的函数指针类型 的函数指针类型的数组。
哈哈,是不是读起来很绕口,很多时候确实是这样的,为了便于理解,也可参考下面的代码:
#include <stdio.h>
typedef char * (*f1_ptr)();
typedef f1_ptr (*f2_ptr)();
char *f1() {
return "hello";
}
f1_ptr f2() {
return f1;
}
int main(int argc, char *argv[])
{
char *(*(*a[1])())();
a[0] = f2;
printf("%s liubang\n", (a[0]())());
return 0;
}
此外,我们还可以借助一些很好用的开源工具来帮我们解读这些声明,而且有时候,用英文表达这些声明能更好的帮我们解:
liubang@venux:~$ sudo apt-get install cdecl -y
liubang@venux:~$ cdecl
Type `help' or `?' for help
cdecl> explain char *(*(*a[])())()
declare a as array of pointer to function returning pointer to function returning pointer to char
cdecl> declare a as pointer to function returning struct tag
struct tag (*a)()
评论