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 协议 ,转载请注明出处!