C语言复杂声明

关于复杂声明,网上最多下面这种问法:

用变量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型的指针。清楚这些,对理解复杂声明十分有帮助。

  • 对于前面a、b、c、d比较简单,懂一点C语言的都是可以理解的
  • 对于int *a[10],由于下标运算符[]高于指针运算符*,所以a首先与后面的[10]结合,即a是一个有10个元素的数组,每个元素都是一个int * 类型,所以int *a[10]的意思被解释为一个有10个指针的数组,该指针指向整型数据
  • 对于int (*a)[10],由于由于运算符()比运算符[]优先级高,所以*a先结合,即a是一个指针,该指针指向一个数组,这个数组有10个元素,每个元素是一个int型数据
  • int (*a)(int);把这种情况与int func(int)对比一下,假如a是指向func的,则*a相当于一个指针解引用,所以很容易推出int (*a)(int)是一个函数指针,所指向的函数需要一个int类型的参数,返回值为int型。对初学者来说,int (*a)(int)与int *a(int)比较容易混乱,前者是一个函数指针,如上面所说的,后者是一个函数,该函数需要一个int类型的参数,返回值为int*,它和(int *) a(int)是同一个意思。
  • 对于int (*a[10])();的解释,同样,这是一个有10个元素的数组,每个元素是一个函数指针,所指向的函数返回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和指针》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值