前言
先看一段有趣的函数声明及引用:
//函数声明
void(***party(void (*p[2][2])(const char* name),const char* name))(const char* name)
//函数调用
party(p,"张三")[1][1]("李四");
我们当然不建议大家写这种复杂类型的函数声明,但由于它常成为笔试的重难点,而且分析这种复杂类型的代码对我们理解声明也有很大帮助。
一.理清“四要素”和操作符的优先性
1.四要素
为什么是四个呢?常调试的小伙伴应该知道监视窗口有名称,值,类型三个要素,有时也会调用内存窗口,故完整来说应该是四个。
2.操作符的优先性
在《C陷阱与缺陷》中这么提到:任何C变量的声明都是由两部分组成:类型以及一组类似表达式的声明符。声明符从表面上看与表达式有些类似,对他求值应该返回一个声明中给定类型的结果。(去掉声明符就是类型)
但其实还应该包含操作符,如:
void (*fp)();
fq是声明符,void(*)()是给定的返回类型,计算机的读取顺序其实还取决于操作符的优先性,我们接下来只需记住,优先级顺序() > [] > * 。
除此以外
因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSIC标准允许程序员将上式简写为fp(),但是一定要记住这种写法只是一种简写形式。
在表达式(*fp)0中,*fp两侧的括号非常重要,因为函数运算符()的优先级高于单目运算符*。如果*fp两侧没有括号,那么*p()实际上与*(fp()的含义完全一致,ANSIC把它作为*((*fp)())的简写形式。
二.基础分析
1.指针数组与数组指针
int *p[3];
int (*p)[3];
第一行是指针数组:p先跟 [] 结合,说明p是一个元素个数为3数组,再与 * 结合,说明数组中存放的是指针,最后与int结合,说明存放的指针指向的是int类型的(p的类型是 int*[3] )就p而言,p是存放3个int*数据的数组
第二行是数组指针:p先跟 * 结合,说明p是一个指针,再与 [] 结合,说明指针指向一个元素个数为3数组,最后与int结合,说明数组里存放的是int类型的。(p的类型是是 int[3] * )就p而言,p是一个指向存放3个int类型数据的指针。
2.指针函数与函数指针
int * pf(int a, int b);
int (*pf)(int a, int b);
第一行是指针函数,pf首先与()结合,说明他是一个函数,参数是有两个且都是int类型的,再与*结合,说明返回一个指针,最后与int结合,说明返回的指针指向一个int的数据(函数返回的是int*的数据)(pf的类型是int *(int,int) ) 就pf而言,他是一个参数为两个int数据,返回值为指向int数据的指针(int*)
第二行是函数指针,pf首先与 * 结合,说明pf是一个指针,再与()结合,说明指针指向的是一个函数,且参数是两个int类型的数据,最后与int结合,说明指向函数的返回类型是int(pf的类型是int (*)(int,int) 就pf而言,他是一个指向一个参数为两个int类型数据,返回值是int类型的函数的指针。
3.深入理解二级指针
int **p;
p先与右边的*结合,说明p是一个指针,再与左边的*结合,说明p这个指针指向一个指针,最后与int结合,说明指向的这个指针指向一个int类型的数据(p的类型是int**)就p而言,p是一个指向int*类型的指针,
三,进阶分析(来源于《C陷阱与缺陷》)
1.(*(void (*)())0)();
( * ( void (*)() ) 0 )();
先看第一个括号内的第一个括号内的(void (*)()),由上我们知道这是一个类型,考虑到大家可能会反应不过来,就再分析一遍:
进来括号的优先级最高,先读 * ,说明这是一个指针,在于()结合,说明指针指向一个函数,再与void结合,说明指向的函数无返回类型(函数指针类型)
括起来后与右边的0结合说明将0强制转换成函数指针类型
与*结合说明0这个指向无参数无返回类型的函数指针指向另一个指针,再与括号结合说明指向的另一个指针是一个函数,整体是一个表达式,说明正在调用。
2. void (*signal(int , void(*)(int)))(int);
void (* signal(int , void(*)(int) ) )(int);
signal首先与()结合,说明这是一个函数,
一个参数是int类型,另一个参数是void(*)(int)类型
再与*结合,说明返回类型是一个指针,再与()结合,说明返回的这个指针是一个函数,由一个参数,是int类型,最后与void结合,说明返回函数的返回无类型
即signal函数有两个参数,返回一个参数为int,返回无类型的函数指针。
四.回归前言问题
void(***party(void (*p[2][2])(const char* name),const char* name))(const char* name)
party先与()结合说明这是一个函数,我们只需要明确party的参数及返回值,括号内自然是参数,然后与*结合,说明是返回一个指针该指针指向为void(**)(const char* name),至于该类型下面调用再讲
party(p,"张三")[1][1]("李四");
party(p,"张三")看成一个表达式,最终会返回一个类型为void(**)(const char* name)的数据,两个[1]就是解引用这个函数数组,()便是函数调用的意思
最后,来一段代码
#include<stdio.h>
void zhufu1(const char* name)
{
printf("%s:祝你在新的一年里身体健康,无病无灾,喜乐长,安宁久\n",name);
}
void zhufu2(const char* name)
{
printf("%s:祝你在新的一年独占鳌头,龙游浅水、悠然自得,事业有成。\n", name);
}
void zhufu3(const char* name)
{
printf("%s:祝你在新的一年里像龙一样勇往直前,无惧风浪,事业节节高,生活更美好\n", name);
}
void zhufu4(const char* name)
{
printf("%s:祝你在龙年里,像龙一样拥有无比的力量和智慧,克服一切困难,迎接更加美好的明天!", name);
}
void (*p[4])(const char* name) = { zhufu1,zhufu2 , zhufu3, zhufu4};
void(**party(void (*p[4])(const char* name),const char* name))(const char* name)
{
for (int i = 0; i < 4; i++)
{
p[i]("furina");
}
printf("\n");
return p;
}
int main()
{
party(p,"张三")[1]("李四");
return 0;
}
最后祝各位读者新年快乐