为了模拟开机启动时的情形,我们必须设计出一个C语句,以显示调用位于地址0的子例程。调用语句如下:
(*(void (*)())0)();
胆颤了吧?首先我们从函数的声明说起:有如下一个函数
void func(){…}
那么,要想声明一个函数指针,指向这类函数,怎样声明呢?如下:
void (*pf)();
那么,想要将一个值转换成一个指针,指向这类函数,怎么强制转换呢?如下:
(void (*)())value
现在value被转换成了一个指向函数的指针,怎么调用它呢?如下:
((void) (*)()value)();
或 (*(void) (*)()value)();
或 (******(void) (*)value)();
神奇吧,看了上面的函数调用方式!其实你完全可以这样调用函数:
func();
(*func)();
(********func)();
没有任何问题!
对于初学的时候,最容易头晕的就是指针符*到底是什么东西?它是变量的一部分?还是声明类型的一部分?其实,仔细回忆C中标识符的定义规则就知道,指针符*必须是类型声明的一部分,因为变量的声明不能含有指针符号,否则是一个非法的变量!
2.C语言中的声明
对比简单类型的强制转换我们就可以更加明白上面的强制转换,简单类型定义如下:
int a;
char b;
b=(char)a;
int *a;
char *b;
b=(int *)a;
其实,在各种指针之间转换很少见,最常见的就是将void指针转换成各种指针,用过malloc族函数吗?
a=(int *)malloc(sizeof(int));
由于声明符和表达式类似,所以你可以这样声明
int ((a)); //将a声明为一个int型值
int *func(),(*fp)();
前者是一个函数,后者是一个指针,所以千万不要对指针定义成函数了,分不清概念的时候最容易这样
int (*fp)(){
/*do something*/
}
显然,这里将一个指针定义成了一个函数!这是任何编译器都不能容忍的。
结合简单变量的类型,出去声明中的变量,得到的就是这个变量的类型,如float a,那么要将一个变量转换成a的类型,那么只需要
(float)value;即可,同理:
float *b,除去变量b,得到b的类型是(float *)。
float (*fp)();除去变量fp,得到的类型是float (*)(),所以要将变量转换成fp类型的值时,只需要(float (*)())value,即可!这样value即被转换成一个函数指针了,也就像最前面的例子中那样!
如果,更复杂,有声明如下:
float * (*fp)(); //返回一个float指针
其实,这也是唬人的,同理可知:强制转换类型为(float * (*)())value;
3.其他考量
看到第一个例子时,可能想,能不能这样子:(*0)();呢?
不行,因为0是一个数字,而*必须要操作一个指针,而且对于要调用的函数是void型的,所以这个指针应该转换成相应的类型,所以需要将0转成一个指向void返回值的参数为空的函数的指针。
最后,linux内核中的信号处理函数定义如下:
void (*signal(int,void (*)(int)))(int);
首先,将上面的函数声明看成这样void (*p)(int);可知,p是一个函数,所以signal函数的返回类型为一个函数指针,指向的函数类型是
void (*)(int);使用typedef可以简化上面signal函数的声明
typedef void (*HANDLER)(int);
HANDLER signal(int,HANDLER);
解释一下:signal是一个返回函数指针的函数,返回的指针指向的函数类型是void (*)(int);而signal的参数一个是int,另一个是一个函数,类型为void (*)(int),刚好就是最初声明的样子。