C语言中的声明解析规则——数组,指针与函数

时间:2022-02-09 22:18:37
摘要:C语言的申明存在的最大问题是:你无法以一种人们所习惯的自然方式和从左向右阅读一个声明,在引入voliatile和const关键字以后,情况更加糟糕了。由于这些关键字只能出现在声明中,是的声明形式和使用形式完全对上号的例子越来越少了。而C语言中比较绕人的指针数组和数组指针的问题,int *ap[]和int (*ap)[]谁是指针数组,谁又是数组指针?这里面声明的解析规则是什么样的?本文主要为你解答这些疑惑。

      我们来看看下面的语句,和它们对应的编译结果:

int (*ap)[2]={1,2};

/*stringcat.c:6:3: warning: initialization makes pointer from integer without a cast [enabled by default]
14 stringcat.c:6:3: warning: (near initialization for ‘ap’) [enabled by default]
15 stringcat.c:6:3: warning: excess elements in scalar initializer [enabled by default]
16 stringcat.c:6:3: warning: (near initialization for ‘ap’) [enabled by default]
17 */

 int (*ap)[2]=int(*)[2]a;

18 /*stringcat.c:7:16: error: expected expression before ‘int’

20 */


int a[2]={1,2}

 int (*ap)[2]=(int(*)[2])a;//注意,此处申明的不是一个数组而是一个指针

     注意,这个才是正确的,但是你能看出上面声明的是一个指针而不是一个数组吗?


      指针和数组之所以复杂,主要原因就再次:他们的声明和使用形似并不相同。来看看C语言的声明是如何形成的?

      声明器就是标识符以及与它组合在一起的任何指针,函数括号和数组下标等。

      构造声明要遵循以下条件:

函数返回值不能是一个函数:foo()()

函数返回值不能是一个数组:foo()[]

数组里面不能有函数:foo[]()

      但可以这样:

函数可以返回函数指针:int (*fun())()

函数可以返回指向数组的指针:int(*foo())[]

数组里面允许函数指针:int(*foo[])()

数组里面允许其他数组:int foo[][]

      这里面,比较qipa的有三种类别,数组,函数,指针。

一般定义:int a;

数组:int a[5];

函数:int fun();

指针:int *p;

      尤其是数组和函数,我们可以认为他们的变量处在类型修饰符(不过把运算符也算作类型的话)的中间。


      我们接下来看看C语言的优先级规则

A:声明从名字开始读取,然后按照优先级次序依次开始读取

B:优先级从高到低的次序是

       B1:声明中被括号扩起来的部分

       B2:后缀操作符() []

       B3:前缀操作符*

C:const和volatile关键字后面紧跟类型说明符的话(int,long),那么它作用与类型说明符号;反之,它作用与左边挨着的*

       如此,可以分析char * const * (*next)()

具体的分析过程按照上述即可,结果是:next是一个指针,它指向一个函数,这个函数返回另一个指针,该指针是指向类型为char的常量指针。


      然后再来分析以往我们提出的例子就简单多了:int (*ap)[2],ap是一个指针,这个指针指向长度为2的int类型的数组;int *ap[2],ap是一个长度为2的数组,数组中的每个元素是指向int类型的指针。这样就明晰了数组指针和指针数组的区别。


一个关于指针的题目:

#include <stdio.h>

void main()

{

       int *p;

       int a=2;

       unsigned long b=1245048;

       p=&a;

       printf("%d/n",*p);

       printf("%p/n",&a);

       printf("%d/n",&a);

       printf("%d/n",(void*)b);

       //printf("%d/n",*(void*)b);  // 被注释的一行,运行此行会提示错误。

       printf("%d/n",*(int*)b);

       printf("%d/n",*(void**)b);

}    

想想其中的倒数第1和第3行分别表示什么意义?