复杂声明

  • c语言不能返回数组,但是可以返回指向数组的指针
    1. int a(); // 函数声明,返回值为int
    2. int abc[3]; // 一维数组声明
    3. int **abc(); // 函数声明,返回值为int指针的指针
    4. int (*abc)(); // 函数指针,返回值int,参数无
    5. int (*abc)[6]; // 指向int型数组的指针
    6. int *abc(); // 函数声明,返回值int指针
    7. int **(*abc[6])(); // 函数指针数组,返回值int指针的指针,参数无
    8. int **abc[6]; // 二维int指针数组
    9. int *(*abc)[6]; // 指向int型指针数组的指针
    10. int *(*abc())(); // 函数,返回一个函数指针,该函数指针接收无参数且返回int*的函数
    11. // declare abc as function returning pointer to function returning pointer to int
    12. int (*(*abc)())[6]; // 函数指针,接收无参数,且返回一维数组的函数
    13. // declare abc as pointer to function returning pointer to array 6 of int
    14. int *(*(*(*abc)())[6])(); // 函数指针,接收无参数,且返回指向一维函数指针数组的函数,
    15. // 该一维函数指针数组中的函数指针接收无参数且返回值为int*的函数
    16. // declare abc as pointer to function returning pointer to array 6 of
    17. // pointer to function returning pointer to int
    上面是c语言的复杂声明,开发过程中可能会要应对复杂业务场景,而需要复杂声明。
    P.S. :上面的后四个尤其难看懂,不过在工作中,代码力求简洁易懂,代码是给人看的,顺便能够给机器执行

解读方法:右左法

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

详解右左法

右左法则:首先从最里面(某种程度上是第一个)的未定义标识符看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向(碰到左括号,往右读;碰到右括号,往左读)。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

引用解析1

int (func[5])(int p);
提取出各个片段之后为:
func—>[5]—>—>(int p)—>int
由于[]的优先级要高于,所以func先和[5]结合,所以func是一个五个元素的数组。下一步判断数组中的内容。接下来是一个说明这个数组中的内容为指针。下一步判断这个指针的类型,(int *p)表示这个指针为一个函数指针,指向的函数参数为一个int指针,下一步判断返回值,int,说明这个函数返回值的类型是int。
总结:func是一个数组,数组中的每一个元素都是一个函数指针,指向的函数接收一个int指针参数并返回一个int值。

引用解析2

  • 细心可发现返回值是不合法的,因为返回数组

int func(int )[5];
提取出各个片段:
func—>(int )—>[5]—>—>int
func与(int )结合,说明func为一个函数,改函数的参数为int ,下面判断返回值:返回一个数组[5],然后判断数组里面的元素类型,为指针(),指针指向一个int。所以总结:
func是一个接收一个int
的函数,返回值为包含5个元素的数组,数组元素为指针类型,并且指向int类型。
PS:这个例子的逻辑说明没有问题,但是实际上是一个不合法的变量声明,因为这个函数返回了一个数组,实际上,在c中是不允许函数返回值为一个数组的。

引用解析3

char ((f(char (para)(char )))[2])();
解释的顺序还是按照上面的顺序来:
f—>(char (para)(char ))—>—>[2]—>—>()—>—>char
1 2 3 4 5 6 7
看上去挺恐怖的吧,看我一一道来
f与1最先结合,说明f是一个函数,这个函数接收一个char (para)(char )这种类型的参数。
先判断这个参数的类型:para—>
—>(char )—>char
para是一个指针,指向一个接收一个char
并返回一个char的函数。
所以f是一个函数,这个函数接收的参数为指向一个接收一个char 参数并返回一个char的函数。
接下来分析f函数的返回值:2为
,所以返回值为一个指针,接下来分析指向的内容,3为[2],所以指针指向一个包含2个元素的数组,下面分析数组的元素类型,4为,所以数组中的类型为指针,分析指针指向的内容,5为(),所以这个指针指向一个参数为空的函数,这个函数的返回值类型为6(指针),并且指向一个7char类型。
所以综上分析:f是一个函数,这个函数接收一个函数指针作为参数,这个函数指针指向一个接收char 参数,并返回一个char的函数,f的返回值为一个指向一个包含两个元素的数组的指针,数组中包含的元类型为一个函数指针,该函数指针指向一个参数为空,返回一个char 的函数。

黑魔法(解析器):
如果依然看不懂,可以借助cdecl解析器

使用typedef简化复杂声明

使用typedef完全可以使上述声明简单易懂,对于char const (*p[10])()这个声明,
数组p中的元素类型为函数指针,那么可以使用typedef为函数指针取别名

  1. char* const *(*p[10])(); // 这个声明可以用typedef简化
  2. typedef char*const* (*PFN)();
  3. // 然后再定义指针数组:
  4. PFN fnAry[10];
  5. // 那么fnAry和p是等价的。
  6. void (*s(int sig,void(*func)(int)))(int);
  7. // 这个声明,使用typedef简化如下:
  8. typedef void(*PFN1)(int);
  9. PFN1 s(int sig,PFN1 pfn);
  10. // 可以看出使用typedef后,声明得到了极大的简化。

练习:

  1. 写出一个返回int,且接收int函数指针的函数指针

  2. 写出一个返回int,且接收int函数指针的指针数组的函数

资料:
blog1
blog2
blog3:返回特定指针函数
blog4:返回函数指针的函数