这章的复杂声明我并没有想去深入的研究,暂时,只要能看懂,稍微用一些有用的部分即可.因为这个复杂声明实在是复杂的过分.而且对于可读性来说简直是破坏性的.有可能自己之后去看都不知道是什么意思.这个只等我之后需要提高的时候再来深入研究.
这个复杂声明实在是不好解释,我在这里还是摘网上的一些高人写的总结比较好.
在c言语的复杂类型声明中,我们用到3个修饰符:*、()、[]。含义如下:
- 暗示声明一个指针
() 暗示声明一个函数
[] 暗示声明一个数组
c语言允许我们一次使用多个上面所说明的修饰符来申明一个变量,这样我们可以构造出多种多样的类别来。
大家先看下面的例子:
int board[8][8]; //二维数组(一个8个元素的数组,元素内容还是数组)
int *p; //指向指针的指针(一个指针,内容还是一个指针)
int array[10]; //10个元素的数组,元素内容是指针。
int (p)[10]; //一个指针,指向含有10个元素的数组。
int oof[3][4]; //3个元素的数组,存放的内容是指针,指针分别指向4个元素的数组
int (oof)[3][4]; //一个指针指向34 的数组。
看到这里,可能有人会想,你在说些什么哦,怎么看不明白,没有关系,看完下面的3条法则,你在来看上面的内容就很清晰了。
1:离名字越近的修饰符优先级越高
2:[],()优先级比*高
3:用()括起来的表达式的优先级最高。
我用这些法则来解释上面提到的例子,请看
int *foo[3][4]
名字:foo
、[3] 离名字一样近,而[4]离的比他们远,所以、[3]的优先级比[4]高。(法则1)
而 [] 的优先级比*高 (法则2)
优先级关系如下:
[3] > * > [4]
所以int foo[3][4] 是一个3个元素的数组(第一个修饰符[3]),存放内容是指针(第二个修饰符号),指针分别指向4个元素的数组(第三个修饰符[4])
int (*foo)[3][4]
名字:foo
优先级关系如下:(括号括起来的表达式优先级最高)
[3] > [4]
所以一个指针指向3*4 的数组。
先从最简单的开始,逐步加深:
int (func)(int p);
首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int类型的形参,返回值类型是int。
int (func)(int p, int (f)(int));
func被一对括号包含,且左边有一个号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int 和int ()(int)这样的形参,返回值为int类型。再来看一看func的形参int (f)(int),类似前面的解释,f也是一个函数指针,指向的函数具有int类型的形参,返回值为int。
int (func[5])(int p);
func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个,说明func的元素是指针,要注意这里的不是修饰func的,而是修饰func[5]的,原因是[]运算符优先级比高,func先跟[]结合,因此修饰的是func[5]。跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int类型的形参,返回值类型为int。
int ((func)[5])(int p);
func被一个圆括号包含,左边又有一个,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下,就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int形参,返回值为int类型的函数。
int ((*func)(int p))[5];
func是一个函数指针,这类函数具有int类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
要注意有些复杂指针声明是非法的,例如:
int func(void) [5];
func是一个返回值为具有5个int元素的数组的函数。但c语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。
int func5;
func是一个具有5个元素的数组,这个数组的元素都是函数。这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。
一、“right-left”规则
看过《C专家编程》中的分析规则,用起来并不是很舒服,遂在网上寻找,发现还有一个著名的“right-left”规则。规则经翻译总结后如下:
“right-left”规则:
- 规则中符号
- 读作 “指向...的指针”
[] 读作 “...的数组”
() 读作 “返回...的函数”
起始点
找到声明中的标识符(Identifier),它就是你分析的起始点,读作:“$(Identifier)是...”;右边
看你的标识符右边
a) 如果发现“()”,你将知道这是一个函数声明,这时你可以说“$(Identifier)是返回...的函数”;
b) 如果发现“[]”,你将知道这是一个数组声明,这时你可以说“$(Identifier)是...的数组”;
c) 继续向右,直到遇到右边声明结束或者遇到“)”,继续下面。左边
看你的标识符左边
a) 如果碰到的不是我们在0.中定义的符号,则直接说出它;否则按照0.中定义的符号含义说出。继续向左,直到遇到左边声明结束或“(”。重复2和3的步骤,直到声明分析完毕。
二、例子详解
我们从简单到复杂,循序渐进。
[Example 1] int *p[];
- 找到标识符:p,读作:“p是...”;
- 向右看:发现一“[]”,然后遇到右边声明结尾,读作:“p是...的数组”;
- 向左看:发现一“*”, 读作:“p是指向...的指针的数组”;
- 继续向左看:没有发现0.中定义的符号,则分析结束,读作:“p是指向int类型的指针的数组”。
[Example 2] int (func())();
- 找到标识符:func,读作:“func是...”;
- 向右看:发现一“()”,然后遇到“)”,读作:“func是返回...的函数”;
- 向左看:发现一“*”,然后遇到“(”,读作:“func是返回指向...的指针的函数”;
- 向右看:发现一“()”,然后右边声明结束,读作:“func是返回指向返回...的函数的指针的函数”;
- 向左看:发现一“*”,读作:“func是返回指向返回指向...的指针的函数的指针的函数”;
- 向左看:没有发现.中定义的符号,则分析结束,读作:“func是返回指向返回指向int类型的指针的函数的指针的函数”。