C语言-如何读懂数组,指针,函数三者混合的派生类型

Published on
95 16~21 min

数组,指针,函数是C语言中重要的三个概念。这些概念单独放开来都还是很好理解的,但将它们凑在一起将会给程序阅读带来比较大的麻烦。这里将介绍我自己的方法帮助各位读懂这类复杂的类型定义,Let's go!

在看下面的内容前,你需要先了解的有:数组,指针,函数,运算符优先级。

可知指针变量的定义如下:

int *p;    //指向int类型的指针变量p

如何理解这样的声明语句?int代表的是类型,*p像是一个表达式(实际上它不是)。可以这样思考,将*p看作一个整体,往函数返回值的方向想,int代表的即是*p的值类型。因为在另一含有*p的表达式中,*p确实被替换为一个int类型的值,就像int func(void)这个函数,在表达式中使用func()时,它也会被替换为一个int类型的值(函数也是一种特殊的变量)。那么通过这样的思想就可得p是一个指向int类型的指针。根据这个思想,我们就能知道为什么下面这条语句的p是一个指向int类型的指针,而q是一个int类型的变量了:

int *p,q;    //逗号用于分开两个声明式子

运用上面的思想,也可以很容易分辨数组指针和指针数组:

int *p[2];    //指针数组
int (*q)[2];    //数组指针

如果是直观来看还是很容易认错的。在解析前先来看看数组的定义:

int p[2];

对于这个数组定义,按照上面的思想,int p[2]代表p[2]的值类型为int,则pint类型的数组(2个元素)变量。

现在看回上面的两个声明,对于int *p[2],由于[]运算符比*运算符优先级高,式子实际上为int *(p[2]),可得*(p[2])的值类型为int,进一步可得p[2]的值类型为指向int类型的指针,则p是一个指向int类型的指针的数组(2个元素)变量;对于int (*p)[2],可得*(p)[2]的值类型为int,进一步可得(*p)的值类型为int类型的数组(2个元素),则p是一个指向int类型的数组(2个元素)的指针。

下面再来看函数指针的定义:

int (*fp)(void);    //指向返回值为int类型,参数为void类型的函数的指针

还是同样的思想,对于上面的int (*fp)(void),可得(*fp)(void)的值类型为int,由于()运算符的结合律是从左到右,可得(*fp)的值类型为返回值为int类型,参数为void类型的函数(为什么()能判定为函数?是因为括号里放的是类型,而不是变量名),则fp是一个指向返回值为int类型,参数为void类型的函数的指针。

总结这一思想,不难得出优先级最低在前面,优先级最高在后面。例如int (*p)[2],[]代表数组成分,*代表指针成分,那么[]优先级低,所以排在前面,*优先级高,所以排在后面,最终可得这是一个数组指针。不过,具体类型还是需要再细读确定的,这只是一个定性的方法。

如何完整的解读出类型呢?这里通过解读C标准库<signal.h>中的函数signal()的声明来举例:

void (*signal(int sig,void (*func)(int)))(int);

先来定性。从优先级低到高,其中(类型)代表函数成分,[]代表数组成分,*代表指针成分,那么这是一个函数指针函数。接下来细读这条声明语句,首先函数指针函数的第一个函数void (...)(int)是一个返回值为void类型,参数为int类型的函数。然后剥去第一个函数和*运算符可得signal(int sig,void (*func)(int))的值类型是指向第一个函数类型的指针。最后剥去函数括号可得signal是一个返回值为指向返回值为void类型,参数为int类型的函数的指针,参数为int类型和func的类型的函数。(这里还有个问题是func的类型,很明显func是一个函数指针,具体是什么就留给读者自己去思考)

总结一下上面的思路:

  • 先定性,大概确定类型(数组,指针,指针的排列)。

  • 对排列从左到右依次剥开解读。

  • 通过再简化上面的思路可以得出,函数的返回值承接前面类型,参数则由()补齐;指针指向承接前面类型;数组元素类型承接前面类型,元素个数则由[]补齐。

下面再来几个例子加深印象:

int *p(int);

式子实际上为int *(p(int))定性可得指针函数。(p(int))的值类型为指向int类型的指针,函数p返回值承接前面指针类型,参数由(int)补齐。可得p是一个返回值为指向int类型的指针,参数为int类型的函数。

int (*p[2])(int);

定性可得函数指针数组。(*p[2])的值类型为返回值为int,参数为int类型的函数,指针指向承接前面函数类型,数组元素承接前面指针类型,元素个数由[2]补齐为2。可得p是一个指向返回值为int类型,参数为int类型的函数的指针的数组。

int (*(*p)(int))(int);

式子实际上为int (*((*p)(int)))(int)定性可得函数指针函数指针。第一个函数(*((*p)(int)))的值类型为返回值为int类型,参数为int类型的函数,指针指向承接前面函数类型,第二个函数的返回值承接前面指针类型,参数由(int)补齐,指针指向承接前面函数类型。可得p是一个指向返回值为指向返回值int类型参数为int类型的函数的指针,参数为int类型的函数的指针。

参考文献:
1.《C陷阱与缺陷》- Andrew Koenig


Prev Post 嵌入式Linux驱动教程PART1-设备与驱动模型
Next Post 矩阵快速幂与矩阵递推加速