关于复杂声明,网上最多下面这种问法:
用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
d)一个有10个整型数的数组( An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
对上面的答案做一点解释
任何C变量的声明都由两部分组成,类型和标识符。对标识符求值应该返回一个标识符类型的结果。如声明int a; 则对a求值应返回整型int,即表达式a返回整型。延伸一点,对于声明float func(); 则对func()来说,其返回值为float型。再延伸一点,对于声明int *p; 则*p的返回值为int,这里则可以说明p是一个指向int型的指针。清楚这些,对理解复杂声明十分有帮助。
下面做一点拓展
拓展1:声明一个函数指针,所指向的函数有2个参数分别为int型和一个函数指针,并且这个函数的返回值也是一个函数指针,作为参数和返回值的这两个函数指针所指向的函数都有两个int型参数,并且返回值也为int型。
我们一步步来分析解决这个问题。
①、由题意可知我们要声明一个函数指针,所指向的函数原型如下:
②、由上面第g点可知,函数指针的原型为int (*a)(int);在这里a是一个实体对象,就好像我们声明int a;一样,a是一个对象,它的类型为int,那么我们如何得到函数指针的类型呢?这里有一个方法,对于函数指针int (*a)(int);,我们要得到这种函数指针类型,只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分有一个括号整个“封装起来”(这句话参考《C陷阱与缺陷》),也就是(int (*)(int)),这就是函数指针的类型。根据题目要求,函数参数和返回值的函数指针所指向的函数有两个int型参数,并且返回的也是int型,所以这种函数指针类型可写为:(int (*)(int, int))
③、根据第①点和第②点,我们可以把函数原型整理为如下,设函数名为func
④、第③点仅仅是一个函数原型,但根据题目要求,我们是要声明一个函数指针,这个指针指向第③点所示的函数,设这个函数指针名为pfunc,如下
⑤、第④点写出来的函数指针是根据常规思路得出来的结果,但最终的结果我们要写为
即把第④点的后面部分移到前面,如下示意图
拓展2:声明一个返回函数指针的函数,该函数指针所指向的函数返回int
有了上面拓展1的分析,这里就简单多了,如下
int (*func())();
拓展3:声明一个返回函数指针的指针的函数,该函数指针所指向的函数返回int
结果如下
int (*(*func()))();
可简化为如下
int (**func())();
拓展4:声明一个返回函数指针的函数,该函数指针所指向的函数返回int的指针
结果如下
int * (*func())();
拓展5:声明一个函数指针,该函数指针所指向的函数返回一个函数指针的指针,返回的这个函数指针所指向的函数返回int,简短一点可以 表达成这样,声明一个返回值为“返回值为int的函数指针的指针”的函数指针
结果如下:
int (*(*(*fun)()))();
可简写为如下:
int (**(*fun)())();
拓展6:声明一个函数指针,该函数指针所指向的函数返回一个函数指针,返回的这个函数指针所指向的函数返回一个int型指针
结果如下:
int * (*(*func)())();
拓展7:声明一个数组指针,该指针指向的数组有10个元素,每个元素又是一个指向有10个整型元素的数组的指针。
结果如下:
int (*a[10])[10];
拓展7:声明一个函数指针,该函数指针所指向的函数返回一个数组指针,该数组指针所指向的数组有10个元素,每个元素都是int型
结果如下:
int (*(*func)())[10];
拓展8:声明一个函数指针,该函数指针所指向的函数返回一个数组指针,该数组指针所指向的数组有10个元素,每个元素都是一个指向int型的指针
结果如下:
int *(*(*func)())[10];
拓展9:声明一个函数指针,该函数指针所指向的函数返回一个数组指针,该数组指针所指向的数组有10个元素,每个元素都是一个函数指针,这些函数指针所指向的函数返回一个int型的指针
int *(*(*(*func)())[10])()
其实到拓展9我也不知道了
声明了这么复杂的声明,那到底这些声明应该怎么用呢?之前在一本经典书籍看到一句话,忘了是哪一本,C的调用和它的声明是一样的。看到这句话,想必是海阔天空了,我们来举一些例子证实一下这个说法。
举例之前,先要了解一个概念,左值和右值。
简单地说,左值就是在等号左边,右值在等号右边,左值对应一个内存空间,而右值仅仅是作为一个值,如
int a;
int b;
a = 5;
b = a;
a = 5;这个表达式中a就是左值,a就要占用一定的内存空间,5就是右值,把右值赋值给a所在的空间。
b = a;这个表达式中a就是右值,而b是左值,把a的值赋值给b所在的内存空间。
继续上面的证实,先从最简单的说起,如对于声明char a;我们要调用a是不是直接使用a就行了,比如用a作为左值来赋值。复杂一点,如下
float f1, f2;
float *p;
p = &f1;
f2 = *p;//此处调用p所指向的单元的值正如它声明的形式一样
再复杂一点,如下
int add(int a, int b)
{
……
}
int mul(int a, int b)
{
……
}
int main( void )
{
int (*padd)(int,int);
int (*pmul)(int, int);
int a;
int b;
padd = add;
pmul = mul;
if( (*padd)(a, b) > (*pmul)(a, b) )//此处对函数声明的调用正如它声明中的形式一样
{
……
}
return 0;
}
经过上面这么多例子,我们就可知道复杂声明的调用也太简单了,不过前提是你要能写出这个声明。
最后,以一个例子来结束本文。
我们知道怎么声明了,知道怎么调用了,但是我们知道这些复杂声明什么时候用吗?用在哪里呢?其实这些东西要做项目多,多参考牛人的程序才能融会贯通吧!下面举一个会用到的例子。玩过进程间通信的编程人员都知道进程间信号通信的方式。这种方式有一个信号处理函数。该函数的原型如下:
void (*signal(int signum, void (*)(int)))(int);
看起来挺复杂的,我们把它裁剪一下,先去掉参数
void (*signal())();
这就一目了然了,和上面的拓展4有点类似,这是一个函数,函数名为signal,函数的参数为一个整型int signum和一个函数指针类型void (*)(int),函数的返回值是一个函数指针类型void (*)(int),这个函数指针所指向的函数有一个参数int,返回值为void
C语言提供typedef可以简化上面的函数声明
typedef void (*HANDLER)(int)
HANDLER signal(int, HANDLER);
对于typedef void (*HANDLER)(int),可以这样理解在以后使用HANDLER的地方,相当于一个函数指针类型void (*)(int)
那么如何使用这个函数呢?如下
void signal_func( int signum )
{
……
}
int main( void )
{
……
signal(SIGQUIT, signal_func);
return 0;
}
对于上面main函数里面signal函数的调用,不是说调用和它的声明形式一样吗?为什么不是呢?注意这里signal是一个函数,不是一个指针,调用这个函数但忽略它的返回值。
参考文献:
1、《C陷阱与缺陷》
2、《C和指针》