C语言复杂声明
本文最后更新于:3 个月前
C语言常常因为声明的语法问题而受到人们的批评,特别是涉及到函数指针的语法。C语言的语法力图使声明和使用相一致。对于简单的情况, C语言的做法是很有效的,但是,如果情况比较复杂,则容易让人混淆,原因在于, C语言的声明不能从左至右阅读,而且使用了太多的圆括号。
在C中,声明的形式为(dcl是declaration的简写):
1 |
|
简而言之,声明符dc1
(可以理解成间接声明)就是前面可能带有多个*
的direcr-dclo
。 direct-dcl
可以是name
、由一对圆括号括起来的dcl
、后面跟有一对圆括号的direct-dcl
、后面跟有用方括号括起来的表示可选长度的direc-dcl
。
根据该规则进行逆向解析,就可以得到正确的声明。简化一下:TypeName Declarator;
其中,Declarator
就是声明中的那个name
。当你遇到任何你不能理解的声明时,这个法则就是救命稻草。最简单的例子:
1 |
|
这里,int
是TypeName
,aInt
是Declarator
。
再说明一下结合紧密度。在声或定义变量时,可以使用一些修饰比如*
,[]
,()
等。()
(非函数声明中的()
)具有最高的紧密度,其次才是函数和数组的()
和[]
。
没有*
的声明称为直接声明(direct-dcl
),而有*
称为声明(dcl
)。直接声明要比声明结合的紧。分解声明时,先读出结合紧的。在这里,我把direct-dcl
称为更紧的结合,它比dcl
结合得紧。
最后,需要你用英语来读出这个声明。对于[]
,应该读成array of
。
对于复杂的定义,可以将其分解。比如T (*p)()
可以分解成T D1()
,D1
读作:*function returning T
。其中D1
是*p
。那么该声明应该读成:p is a poniter to
*。二者合在一起,就变成了 *p is a pointer to function returning T
*,即:p
是指向返回T
类对象的函数的指针。
再看一个稍微复杂的示例:
1 |
|
根据dcl
和direct-dcl
,可以分解成T1 D1
(因为结合紧密度),T1
也就是T ()
,那么应该读作:
*D1 is function returning T
*。
D1
又可以写成T2 D2
,其中T2
是T1 []
,可以分解成T1 D2[]
,读作:*array of D2 function returning T
*。
D2
是指针,读作:*pointers to
。那么整个 T (*pfa[])()
应该读作:pfa is an array of pointers to function returning T
*,即:pfa
是个存放指向返回T类对象函数的指针的数组。
换种方式看,在这个例子中,pfa
是名字,T(*[])()
是类型。将(*pfa[])
视为一体(direct-dcl
),称为D1
,那么可以写成T D1()
,*function returning object of T
*。在D1
中,将*pfa
视为一体(dcl
),称为D2
,那么*pfa[]
应该是D2[]
(direct-dcl),array of D2
。合起来就是 *array of D2 function returning object of T
*。D2
是*pfa
(dcl),替换到前面这句话,结果就是 *array of pointers to function returning object of T
*。
有了这些说明,可以试着做一下下面的题,看看自己是否真的理解了:
1 |
|
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
1 |
|
首先找到变量名func
,外面有一对圆括号,而且左边是一个*
号,这说明func
是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)
是一个函数,所以func
是一个指向这类函数的指针,即函数指针,这类函数具有int*
类型的形参,返回值类型是int
。
1 |
|
func
右边是一个[]
运算符,说明func
是具有5
个元素的数组;func
的左边有一个*
,说明func
的元素是指针(注意这里的*
不是修饰func
,而是修饰func[5]
的,原因是[]
运算符优先级比*
高,func
先跟[]
结合)。跳出这个括号,看右边,又遇到圆括号,说明func
数组的元素是函数类型的指针,它指向的函数具有int*
类型的形参,返回值类型为int
。
在C++中,规则比C要复杂一些。不过,基本思想保持不变,按照C的原则来理解复杂的声明,基本上就能满足要求了。没有在这里列出C++的规则一方面是因为太广,不能覆盖全;另一个原因就是,按照C的规则来就足够了,毕竟C++要与C兼容。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!