C语言-如何读懂数组,指针,函数三者混合的派生类型
数组,指针,函数是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
,则p
是int
类型的数组(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